aboutsummaryrefslogtreecommitdiff
path: root/lib/libc/tests
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libc/tests')
-rw-r--r--lib/libc/tests/Makefile41
-rw-r--r--lib/libc/tests/Makefile.depend10
-rw-r--r--lib/libc/tests/Makefile.netbsd-tests7
-rw-r--r--lib/libc/tests/c063/Makefile22
-rw-r--r--lib/libc/tests/c063/Makefile.depend18
-rw-r--r--lib/libc/tests/db/Makefile22
-rw-r--r--lib/libc/tests/db/Makefile.depend18
-rw-r--r--lib/libc/tests/db/dbm_nextkey_test.c53
-rw-r--r--lib/libc/tests/db/dbm_open_test.c52
-rw-r--r--lib/libc/tests/db/dbm_perm_test.c98
-rw-r--r--lib/libc/tests/gen/Makefile136
-rw-r--r--lib/libc/tests/gen/Makefile.depend21
-rw-r--r--lib/libc/tests/gen/arc4random_test.c112
-rw-r--r--lib/libc/tests/gen/dir2_test.c186
-rw-r--r--lib/libc/tests/gen/dlopen_empty_test.c94
-rw-r--r--lib/libc/tests/gen/execve/Makefile7
-rw-r--r--lib/libc/tests/gen/execve/Makefile.depend18
-rw-r--r--lib/libc/tests/gen/fmtcheck_test.c102
-rw-r--r--lib/libc/tests/gen/fmtmsg_test.c249
-rw-r--r--lib/libc/tests/gen/fnmatch_test.c266
-rw-r--r--lib/libc/tests/gen/fnmatch_testcases.h173
-rw-r--r--lib/libc/tests/gen/fpclassify2_test.c70
-rw-r--r--lib/libc/tests/gen/fts_blocks_test.c68
-rw-r--r--lib/libc/tests/gen/fts_misc_test.c78
-rw-r--r--lib/libc/tests/gen/fts_options_test.c394
-rw-r--r--lib/libc/tests/gen/fts_test.h81
-rw-r--r--lib/libc/tests/gen/ftw_test.c124
-rw-r--r--lib/libc/tests/gen/getentropy_test.c85
-rw-r--r--lib/libc/tests/gen/getmntinfo_test.c83
-rw-r--r--lib/libc/tests/gen/glob2_test.c184
-rw-r--r--lib/libc/tests/gen/glob_blocks_test.c62
-rw-r--r--lib/libc/tests/gen/makecontext_test.c185
-rw-r--r--lib/libc/tests/gen/opendir_test.c145
-rw-r--r--lib/libc/tests/gen/popen_test.c248
-rw-r--r--lib/libc/tests/gen/posix_spawn/Makefile32
-rw-r--r--lib/libc/tests/gen/posix_spawn/Makefile.depend18
-rw-r--r--lib/libc/tests/gen/posix_spawn_test.c186
-rw-r--r--lib/libc/tests/gen/realpath2_test.c99
-rw-r--r--lib/libc/tests/gen/scandir_blocks_test.c118
-rw-r--r--lib/libc/tests/gen/scandir_test.c195
-rw-r--r--lib/libc/tests/gen/sig2str_test.c213
-rw-r--r--lib/libc/tests/gen/sigsetops_test.c188
-rwxr-xr-xlib/libc/tests/gen/spawnp_enoexec.sh3
-rw-r--r--lib/libc/tests/gen/test-fnmatch.c99
-rw-r--r--lib/libc/tests/gen/wordexp_test.c383
-rw-r--r--lib/libc/tests/hash/Makefile37
-rw-r--r--lib/libc/tests/hash/Makefile.depend20
-rw-r--r--lib/libc/tests/iconv/Makefile5
-rw-r--r--lib/libc/tests/iconv/Makefile.depend17
-rw-r--r--lib/libc/tests/iconv/iconvctl_test.c65
-rw-r--r--lib/libc/tests/inet/Makefile8
-rw-r--r--lib/libc/tests/inet/Makefile.depend19
-rw-r--r--lib/libc/tests/locale/Makefile44
-rw-r--r--lib/libc/tests/locale/Makefile.depend19
-rw-r--r--lib/libc/tests/locale/btowc_test.c68
-rw-r--r--lib/libc/tests/locale/c16rtomb_test.c163
-rw-r--r--lib/libc/tests/locale/iswctype_test.c119
-rw-r--r--lib/libc/tests/locale/mblen_test.c113
-rw-r--r--lib/libc/tests/locale/mbrlen_test.c123
-rw-r--r--lib/libc/tests/locale/mbrtoc16_test.c211
-rw-r--r--lib/libc/tests/locale/mbrtowc_test.c162
-rw-r--r--lib/libc/tests/locale/mbsnrtowcs_test.c192
-rw-r--r--lib/libc/tests/locale/mbsrtowcs_test.c150
-rw-r--r--lib/libc/tests/locale/mbstowcs_test.c108
-rw-r--r--lib/libc/tests/locale/mbtowc_test.c119
-rw-r--r--lib/libc/tests/locale/newlocale_test.c111
-rw-r--r--lib/libc/tests/locale/towctrans_test.c87
-rw-r--r--lib/libc/tests/locale/wcrtomb_test.c132
-rw-r--r--lib/libc/tests/locale/wcsnrtombs_test.c192
-rw-r--r--lib/libc/tests/locale/wcsrtombs_test.c153
-rw-r--r--lib/libc/tests/locale/wcstombs_test.c127
-rw-r--r--lib/libc/tests/locale/wctomb_test.c123
-rw-r--r--lib/libc/tests/net/Makefile43
-rw-r--r--lib/libc/tests/net/Makefile.depend19
-rw-r--r--lib/libc/tests/net/ether_test.c190
-rw-r--r--lib/libc/tests/net/eui64_aton_test.c102
-rw-r--r--lib/libc/tests/net/eui64_ntoa_test.c62
-rw-r--r--lib/libc/tests/net/getaddrinfo/Makefile47
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/basics_v4.exp42
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/basics_v4_only.exp50
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/basics_v4v6.exp50
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/basics_v4v6_prefer_v4.exp50
-rwxr-xr-xlib/libc/tests/net/getaddrinfo/data/generate_testdata.sh45
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/no_host_v4.exp44
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/no_host_v4_only.exp68
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/no_host_v4v6.exp68
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/no_host_v4v6_prefer_v4.exp68
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/no_serv_v4.exp17
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/no_serv_v4_only.exp20
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/no_serv_v4v6.exp20
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/no_serv_v4v6_prefer_v4.exp20
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/scoped.exp5
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/scoped_v4_only.exp5
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/scoped_v4v6_prefer_v4.exp5
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/sock_raw_v4.exp13
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/sock_raw_v4_only.exp15
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/sock_raw_v4v6.exp15
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/sock_raw_v4v6_prefer_v4.exp15
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/spec_fam_v4.exp7
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/spec_fam_v4_only.exp10
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/spec_fam_v4v6.exp10
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/spec_fam_v4v6_prefer_v4.exp10
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/unsup_fam.exp2
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/unsup_fam_v4_only.exp2
-rw-r--r--lib/libc/tests/net/getaddrinfo/data/unsup_fam_v4v6_prefer_v4.exp2
-rw-r--r--lib/libc/tests/net/getaddrinfo/getaddrinfo.c271
-rwxr-xr-xlib/libc/tests/net/getaddrinfo/getaddrinfo_test.sh443
-rw-r--r--lib/libc/tests/net/link_addr_test.cc532
-rw-r--r--lib/libc/tests/net/test-eui64.h53
-rw-r--r--lib/libc/tests/nss/Makefile25
-rw-r--r--lib/libc/tests/nss/Makefile.depend19
-rw-r--r--lib/libc/tests/nss/getaddrinfo_test.c554
-rw-r--r--lib/libc/tests/nss/getgr_test.c574
-rw-r--r--lib/libc/tests/nss/gethostby_test.c1510
-rw-r--r--lib/libc/tests/nss/getproto_test.c553
-rw-r--r--lib/libc/tests/nss/getpw_test.c555
-rw-r--r--lib/libc/tests/nss/getrpc_test.c555
-rw-r--r--lib/libc/tests/nss/getserv_test.c571
-rw-r--r--lib/libc/tests/nss/getusershell_test.c221
-rw-r--r--lib/libc/tests/nss/testutil.h328
-rw-r--r--lib/libc/tests/regex/Makefile8
-rw-r--r--lib/libc/tests/regex/Makefile.depend19
-rw-r--r--lib/libc/tests/regex/Makefile.inc56
-rwxr-xr-xlib/libc/tests/regex/multibyte.sh93
-rw-r--r--lib/libc/tests/resolv/Makefile13
-rw-r--r--lib/libc/tests/resolv/Makefile.depend18
-rw-r--r--lib/libc/tests/resolv/mach45
-rw-r--r--lib/libc/tests/resolv/resolv_test.c354
-rw-r--r--lib/libc/tests/rpc/Makefile23
-rw-r--r--lib/libc/tests/rpc/Makefile.depend21
-rw-r--r--lib/libc/tests/secure/Makefile39
-rw-r--r--lib/libc/tests/secure/fortify_poll_test.c617
-rw-r--r--lib/libc/tests/secure/fortify_random_test.c316
-rw-r--r--lib/libc/tests/secure/fortify_select_test.c769
-rw-r--r--lib/libc/tests/secure/fortify_signal_test.c316
-rw-r--r--lib/libc/tests/secure/fortify_socket_test.c1971
-rw-r--r--lib/libc/tests/secure/fortify_stdio_test.c1559
-rw-r--r--lib/libc/tests/secure/fortify_stdlib_test.c610
-rw-r--r--lib/libc/tests/secure/fortify_string_test.c2271
-rw-r--r--lib/libc/tests/secure/fortify_strings_test.c615
-rw-r--r--lib/libc/tests/secure/fortify_uio_test.c917
-rw-r--r--lib/libc/tests/secure/fortify_unistd_test.c2199
-rw-r--r--lib/libc/tests/secure/fortify_wchar_test.c2124
-rwxr-xr-xlib/libc/tests/secure/generate-fortify-tests.lua1581
-rw-r--r--lib/libc/tests/setjmp/Makefile10
-rw-r--r--lib/libc/tests/setjmp/Makefile.depend18
-rw-r--r--lib/libc/tests/ssp/Makefile60
-rw-r--r--lib/libc/tests/ssp/Makefile.depend20
-rw-r--r--lib/libc/tests/stdio/Makefile55
-rw-r--r--lib/libc/tests/stdio/Makefile.depend19
-rw-r--r--lib/libc/tests/stdio/eintr_test.c143
-rw-r--r--lib/libc/tests/stdio/fdopen_test.c222
-rw-r--r--lib/libc/tests/stdio/flushlbuf_test.c155
-rw-r--r--lib/libc/tests/stdio/fmemopen2_test.c318
-rw-r--r--lib/libc/tests/stdio/fopen_test.c202
-rw-r--r--lib/libc/tests/stdio/freopen_test.c220
-rw-r--r--lib/libc/tests/stdio/getdelim_test.c431
-rw-r--r--lib/libc/tests/stdio/gets_s_test.c142
-rw-r--r--lib/libc/tests/stdio/mkostemp_test.c182
-rw-r--r--lib/libc/tests/stdio/open_memstream2_test.c198
-rw-r--r--lib/libc/tests/stdio/open_wmemstream_test.c198
-rw-r--r--lib/libc/tests/stdio/perror_test.c104
-rw-r--r--lib/libc/tests/stdio/print_positional_test.c154
-rw-r--r--lib/libc/tests/stdio/printbasic_test.c159
-rw-r--r--lib/libc/tests/stdio/printfloat_test.c432
-rw-r--r--lib/libc/tests/stdio/scanfloat_test.c313
-rw-r--r--lib/libc/tests/stdio/snprintf_test.c205
-rw-r--r--lib/libc/tests/stdio/sscanf_test.c356
-rw-r--r--lib/libc/tests/stdio/swprintf_test.c206
-rw-r--r--lib/libc/tests/stdio/swscanf_test.c359
-rw-r--r--lib/libc/tests/stdlib/Makefile85
-rw-r--r--lib/libc/tests/stdlib/Makefile.depend24
-rw-r--r--lib/libc/tests/stdlib/clearenv_test.c173
-rw-r--r--lib/libc/tests/stdlib/cxa_atexit_test.c132
-rw-r--r--lib/libc/tests/stdlib/cxa_thread_atexit_nothr_test.cc99
-rw-r--r--lib/libc/tests/stdlib/cxa_thread_atexit_test.cc177
-rw-r--r--lib/libc/tests/stdlib/dynthr_mod/Makefile10
-rw-r--r--lib/libc/tests/stdlib/dynthr_mod/dynthr_mod.c71
-rw-r--r--lib/libc/tests/stdlib/dynthr_test.c90
-rw-r--r--lib/libc/tests/stdlib/getenv_r_test.c69
-rw-r--r--lib/libc/tests/stdlib/heapsort_test.c68
-rw-r--r--lib/libc/tests/stdlib/libatexit/Makefile11
-rw-r--r--lib/libc/tests/stdlib/libatexit/libatexit.cc67
-rw-r--r--lib/libc/tests/stdlib/libc_exit_test.c154
-rw-r--r--lib/libc/tests/stdlib/mergesort_test.c68
-rw-r--r--lib/libc/tests/stdlib/qsort_b_test.c76
-rw-r--r--lib/libc/tests/stdlib/qsort_bench.c113
-rw-r--r--lib/libc/tests/stdlib/qsort_r_compat_test.c89
-rw-r--r--lib/libc/tests/stdlib/qsort_r_test.c89
-rw-r--r--lib/libc/tests/stdlib/qsort_s_test.c254
-rw-r--r--lib/libc/tests/stdlib/qsort_test.c68
-rw-r--r--lib/libc/tests/stdlib/set_constraint_handler_s_test.c60
-rw-r--r--lib/libc/tests/stdlib/strfmon_test.c252
-rw-r--r--lib/libc/tests/stdlib/test-sort.h271
-rw-r--r--lib/libc/tests/stdlib/tsearch_test.c156
-rw-r--r--lib/libc/tests/stdtime/Makefile13
-rw-r--r--lib/libc/tests/stdtime/detect_tz_changes_test.c477
-rw-r--r--lib/libc/tests/stdtime/strptime_test.c50
-rw-r--r--lib/libc/tests/string/Makefile58
-rw-r--r--lib/libc/tests/string/Makefile.depend19
-rw-r--r--lib/libc/tests/string/bcmp_test.c32
-rw-r--r--lib/libc/tests/string/ffs_test.c87
-rw-r--r--lib/libc/tests/string/ffsl_test.c32
-rw-r--r--lib/libc/tests/string/ffsll_test.c32
-rw-r--r--lib/libc/tests/string/fls_test.c87
-rw-r--r--lib/libc/tests/string/flsl_test.c32
-rw-r--r--lib/libc/tests/string/flsll_test.c32
-rw-r--r--lib/libc/tests/string/memccpy_test.c229
-rw-r--r--lib/libc/tests/string/memcmp_test.c160
-rw-r--r--lib/libc/tests/string/memrchr_test.c116
-rw-r--r--lib/libc/tests/string/memset_s_test.c197
-rw-r--r--lib/libc/tests/string/memset_test.c29
-rw-r--r--lib/libc/tests/string/stpncpy_test.c181
-rw-r--r--lib/libc/tests/string/strcmp_test.c132
-rw-r--r--lib/libc/tests/string/strcspn_test.c269
-rw-r--r--lib/libc/tests/string/strerror_test.c204
-rw-r--r--lib/libc/tests/string/strlcpy_test.c183
-rw-r--r--lib/libc/tests/string/strncmp_test.c165
-rw-r--r--lib/libc/tests/string/strnlen_test.c141
-rw-r--r--lib/libc/tests/string/strspn_test.c30
-rw-r--r--lib/libc/tests/string/strverscmp_test.c93
-rw-r--r--lib/libc/tests/string/strxfrm_test.c48
-rw-r--r--lib/libc/tests/string/timingsafe_bcmp_test.c32
-rw-r--r--lib/libc/tests/string/timingsafe_memcmp_test.c32
-rw-r--r--lib/libc/tests/string/wcscasecmp_test.c125
-rw-r--r--lib/libc/tests/string/wcscoll_test.c153
-rw-r--r--lib/libc/tests/string/wcsnlen_test.c104
-rw-r--r--lib/libc/tests/sys/Makefile105
-rw-r--r--lib/libc/tests/sys/Makefile.depend22
-rw-r--r--lib/libc/tests/sys/brk_test.c146
-rw-r--r--lib/libc/tests/sys/cpuset_test.c691
-rw-r--r--lib/libc/tests/sys/errno_test.c36
-rw-r--r--lib/libc/tests/sys/mlock_helper.c111
-rw-r--r--lib/libc/tests/sys/queue_test.c234
-rw-r--r--lib/libc/tests/sys/sendfile_test.c1208
-rw-r--r--lib/libc/tests/sys/swapcontext_test.c63
-rw-r--r--lib/libc/tests/termios/Makefile7
-rw-r--r--lib/libc/tests/termios/Makefile.depend18
-rw-r--r--lib/libc/tests/time/Makefile8
-rw-r--r--lib/libc/tests/time/Makefile.depend18
-rw-r--r--lib/libc/tests/tls/Makefile31
-rw-r--r--lib/libc/tests/tls/Makefile.depend20
-rw-r--r--lib/libc/tests/tls/dso/Makefile16
-rw-r--r--lib/libc/tests/tls/dso/Makefile.depend17
-rw-r--r--lib/libc/tests/tls_dso/Makefile20
-rw-r--r--lib/libc/tests/tls_dso/Makefile.depend14
-rw-r--r--lib/libc/tests/ttyio/Makefile10
-rw-r--r--lib/libc/tests/ttyio/Makefile.depend19
248 files changed, 46187 insertions, 0 deletions
diff --git a/lib/libc/tests/Makefile b/lib/libc/tests/Makefile
new file mode 100644
index 000000000000..975c895770ee
--- /dev/null
+++ b/lib/libc/tests/Makefile
@@ -0,0 +1,41 @@
+.include <src.opts.mk>
+
+SUBDIR= tls_dso
+
+TESTS_SUBDIRS= c063
+TESTS_SUBDIRS+= db
+TESTS_SUBDIRS+= gen
+TESTS_SUBDIRS+= hash
+TESTS_SUBDIRS+= inet
+TESTS_SUBDIRS+= net
+TESTS_SUBDIRS+= nss
+TESTS_SUBDIRS+= regex
+TESTS_SUBDIRS+= resolv
+TESTS_SUBDIRS+= rpc
+TESTS_SUBDIRS+= secure
+TESTS_SUBDIRS+= setjmp
+TESTS_SUBDIRS+= stdio
+TESTS_SUBDIRS+= stdlib
+TESTS_SUBDIRS+= stdtime
+TESTS_SUBDIRS+= string
+TESTS_SUBDIRS+= sys
+TESTS_SUBDIRS+= termios
+TESTS_SUBDIRS+= time
+TESTS_SUBDIRS+= tls
+TESTS_SUBDIRS+= ttyio
+
+SUBDIR_DEPEND_tls= tls_dso
+
+.if ${MK_ICONV} != "no"
+TESTS_SUBDIRS+= iconv
+.endif
+
+.if ${MK_LOCALES} != "no"
+TESTS_SUBDIRS+= locale
+.endif
+
+.if ${MK_SSP} != "no"
+TESTS_SUBDIRS+= ssp
+.endif
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/Makefile.depend b/lib/libc/tests/Makefile.depend
new file mode 100644
index 000000000000..11aba52f82cf
--- /dev/null
+++ b/lib/libc/tests/Makefile.depend
@@ -0,0 +1,10 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/Makefile.netbsd-tests b/lib/libc/tests/Makefile.netbsd-tests
new file mode 100644
index 000000000000..43710d39aca2
--- /dev/null
+++ b/lib/libc/tests/Makefile.netbsd-tests
@@ -0,0 +1,7 @@
+TESTSRC:= ${SRCTOP}/contrib/netbsd-tests/${RELDIR:C/libc\/tests/libc/}
+
+TESTSDIR:= ${TESTSBASE}/${RELDIR:C/libc\/tests/libc/}
+
+WARNS?= 2
+
+.include <netbsd-tests.test.mk>
diff --git a/lib/libc/tests/c063/Makefile b/lib/libc/tests/c063/Makefile
new file mode 100644
index 000000000000..c1d8b01c1abd
--- /dev/null
+++ b/lib/libc/tests/c063/Makefile
@@ -0,0 +1,22 @@
+NETBSD_ATF_TESTS_C= faccessat_test
+NETBSD_ATF_TESTS_C+= fchmodat_test
+NETBSD_ATF_TESTS_C+= fchownat_test
+NETBSD_ATF_TESTS_C+= fexecve_test
+NETBSD_ATF_TESTS_C+= fstatat_test
+NETBSD_ATF_TESTS_C+= linkat_test
+NETBSD_ATF_TESTS_C+= mkdirat_test
+NETBSD_ATF_TESTS_C+= mkfifoat_test
+NETBSD_ATF_TESTS_C+= mknodat_test
+NETBSD_ATF_TESTS_C+= o_search_test
+NETBSD_ATF_TESTS_C+= openat_test
+NETBSD_ATF_TESTS_C+= readlinkat_test
+NETBSD_ATF_TESTS_C+= renameat_test
+NETBSD_ATF_TESTS_C+= symlinkat_test
+NETBSD_ATF_TESTS_C+= unlinkat_test
+NETBSD_ATF_TESTS_C+= utimensat_test
+
+CFLAGS+= -D_INCOMPLETE_XOPEN_C063
+
+.include "../Makefile.netbsd-tests"
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/c063/Makefile.depend b/lib/libc/tests/c063/Makefile.depend
new file mode 100644
index 000000000000..e89a5c52c82a
--- /dev/null
+++ b/lib/libc/tests/c063/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libnetbsd \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/db/Makefile b/lib/libc/tests/db/Makefile
new file mode 100644
index 000000000000..771569183584
--- /dev/null
+++ b/lib/libc/tests/db/Makefile
@@ -0,0 +1,22 @@
+PACKAGE= tests
+
+BINDIR= ${TESTSDIR}
+
+PROGS= h_db
+PROGS+= h_lfsr
+
+${PACKAGE}FILES+= README
+
+ATF_TESTS_C+= dbm_open_test
+ATF_TESTS_C+= dbm_perm_test
+ATF_TESTS_C+= dbm_nextkey_test
+
+NETBSD_ATF_TESTS_C+= db_hash_seq_test
+NETBSD_ATF_TESTS_SH+= db_test
+ATF_TESTS_SH_SED_db_test= -e 's,/bin/csh,/bin/cat,g'
+
+CFLAGS+= -I${SRCTOP}/lib/libc/db/btree
+
+.include "../Makefile.netbsd-tests"
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/db/Makefile.depend b/lib/libc/tests/db/Makefile.depend
new file mode 100644
index 000000000000..e89a5c52c82a
--- /dev/null
+++ b/lib/libc/tests/db/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libnetbsd \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/db/dbm_nextkey_test.c b/lib/libc/tests/db/dbm_nextkey_test.c
new file mode 100644
index 000000000000..67b745efb196
--- /dev/null
+++ b/lib/libc/tests/db/dbm_nextkey_test.c
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <fcntl.h>
+#include <ndbm.h>
+#include <stdio.h>
+
+#include <atf-c.h>
+
+static const char *path = "tmp";
+static const char *dbname = "tmp.db";
+
+ATF_TC(dbm_nextkey_test);
+ATF_TC_HEAD(dbm_nextkey_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Check that dbm_nextkey always returns NULL after reaching the end of the database");
+}
+
+ATF_TC_BODY(dbm_nextkey_test, tc)
+{
+ DBM *db;
+ datum key, data;
+
+ data.dptr = "bar";
+ data.dsize = strlen("bar");
+ key.dptr = "foo";
+ key.dsize = strlen("foo");
+
+ db = dbm_open(path, O_RDWR | O_CREAT, 0755);
+ ATF_CHECK(db != NULL);
+ ATF_REQUIRE(atf_utils_file_exists(dbname));
+ ATF_REQUIRE(dbm_store(db, key, data, DBM_INSERT) != -1);
+
+ key = dbm_firstkey(db);
+ ATF_REQUIRE(key.dptr != NULL);
+ key = dbm_nextkey(db);
+ ATF_REQUIRE(key.dptr == NULL);
+ key = dbm_nextkey(db);
+ ATF_REQUIRE(key.dptr == NULL);
+
+ dbm_close(db);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, dbm_nextkey_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/db/dbm_open_test.c b/lib/libc/tests/db/dbm_open_test.c
new file mode 100644
index 000000000000..8a3e888bf72c
--- /dev/null
+++ b/lib/libc/tests/db/dbm_open_test.c
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <fcntl.h>
+#include <ndbm.h>
+#include <stdio.h>
+
+#include <atf-c.h>
+
+static const char *path = "tmp";
+static const char *dbname = "tmp.db";
+
+ATF_TC(dbm_open_missing_test);
+ATF_TC_HEAD(dbm_open_missing_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test dbm_open when creating a new database");
+}
+
+ATF_TC_BODY(dbm_open_missing_test, tc)
+{
+
+ /*
+ * POSIX.1 specifies that a missing database file should
+ * always get created if O_CREAT is present, except when
+ * O_EXCL is specified as well.
+ */
+ ATF_CHECK(dbm_open(path, O_RDONLY, 0755) == NULL);
+ ATF_REQUIRE(!atf_utils_file_exists(dbname));
+ ATF_CHECK(dbm_open(path, O_RDONLY | O_CREAT, 0755) != NULL);
+ ATF_REQUIRE(atf_utils_file_exists(dbname));
+ ATF_CHECK(dbm_open(path, O_RDONLY | O_CREAT | O_EXCL, 0755) == NULL);
+}
+
+ATF_TC_WITHOUT_HEAD(dbm_open_wronly_test);
+ATF_TC_BODY(dbm_open_wronly_test, tc)
+{
+ ATF_CHECK(dbm_open(path, O_WRONLY, 0755) == NULL);
+ ATF_REQUIRE(!atf_utils_file_exists(dbname));
+ ATF_CHECK(dbm_open(path, O_WRONLY | O_CREAT, 0755) != NULL);
+ ATF_REQUIRE(atf_utils_file_exists(dbname));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, dbm_open_missing_test);
+ ATF_TP_ADD_TC(tp, dbm_open_wronly_test);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/db/dbm_perm_test.c b/lib/libc/tests/db/dbm_perm_test.c
new file mode 100644
index 000000000000..c07210292014
--- /dev/null
+++ b/lib/libc/tests/db/dbm_perm_test.c
@@ -0,0 +1,98 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <ndbm.h>
+#include <stdio.h>
+
+#include <atf-c.h>
+
+static const char *path = "tmp";
+static const char *dbname = "tmp.db";
+
+static void
+create_db(void)
+{
+ DB *db;
+ datum data, key;
+
+ data.dptr = "bar";
+ data.dsize = strlen("bar");
+ key.dptr = "foo";
+ key.dsize = strlen("foo");
+
+ db = dbm_open(path, O_RDWR | O_CREAT, 0755);
+ ATF_CHECK(db != NULL);
+ ATF_REQUIRE(atf_utils_file_exists(dbname));
+ ATF_REQUIRE(dbm_store(db, key, data, DBM_INSERT) != -1);
+ dbm_close(db);
+}
+
+ATF_TC_WITHOUT_HEAD(dbm_rdonly_test);
+ATF_TC_BODY(dbm_rdonly_test, tc)
+{
+ DB *db;
+ datum data, key;
+
+ bzero(&data, sizeof(data));
+ key.dptr = "foo";
+ key.dsize = strlen("foo");
+ create_db();
+
+ db = dbm_open(path, O_RDONLY, 0755);
+ data = dbm_fetch(db, key);
+ ATF_REQUIRE(data.dptr != NULL);
+ ATF_REQUIRE(strncmp((const char*)data.dptr, "bar", data.dsize) == 0);
+ ATF_REQUIRE(dbm_store(db, key, data, DBM_REPLACE) == -1);
+ ATF_REQUIRE(errno == EPERM);
+}
+
+ATF_TC_WITHOUT_HEAD(dbm_wronly_test);
+ATF_TC_BODY(dbm_wronly_test, tc)
+{
+ DB *db;
+ datum data, key;
+
+ key.dptr = "foo";
+ key.dsize = strlen("foo");
+ data.dptr = "baz";
+ data.dsize = strlen("baz");
+ create_db();
+
+ db = dbm_open(path, O_WRONLY, 0755);
+ data = dbm_fetch(db, key);
+ ATF_REQUIRE(data.dptr == NULL);
+ ATF_REQUIRE(errno == EPERM);
+ ATF_REQUIRE(dbm_store(db, key, data, DBM_REPLACE) != -1);
+}
+
+ATF_TC_WITHOUT_HEAD(dbm_rdwr_test);
+ATF_TC_BODY(dbm_rdwr_test, tc)
+{
+ DB *db;
+ datum data, key;
+
+ key.dptr = "foo";
+ key.dsize = strlen("foo");
+ create_db();
+
+ db = dbm_open(path, O_RDWR, 0755);
+ data = dbm_fetch(db, key);
+ ATF_REQUIRE(data.dptr != NULL);
+ data.dptr = "baz";
+ data.dsize = strlen("baz");
+ ATF_REQUIRE(dbm_store(db, key, data, DBM_REPLACE) != -1);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, dbm_rdonly_test);
+ ATF_TP_ADD_TC(tp, dbm_wronly_test);
+ ATF_TP_ADD_TC(tp, dbm_rdwr_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/Makefile b/lib/libc/tests/gen/Makefile
new file mode 100644
index 000000000000..8c2151105209
--- /dev/null
+++ b/lib/libc/tests/gen/Makefile
@@ -0,0 +1,136 @@
+.include <bsd.own.mk>
+
+ATF_TESTS_C+= arc4random_test
+ATF_TESTS_C+= dir2_test
+ATF_TESTS_C+= dlopen_empty_test
+ATF_TESTS_C+= fmtcheck2_test
+ATF_TESTS_C+= fmtmsg_test
+ATF_TESTS_C+= fnmatch2_test
+ATF_TESTS_C+= fpclassify2_test
+.if ${COMPILER_FEATURES:Mblocks}
+ATF_TESTS_C+= fts_blocks_test
+.endif
+ATF_TESTS_C+= fts_misc_test
+ATF_TESTS_C+= fts_options_test
+ATF_TESTS_C+= ftw_test
+ATF_TESTS_C+= getentropy_test
+ATF_TESTS_C+= getmntinfo_test
+ATF_TESTS_C+= glob2_test
+.if ${COMPILER_FEATURES:Mblocks}
+ATF_TESTS_C+= glob_blocks_test
+.endif
+ATF_TESTS_C+= makecontext_test
+ATF_TESTS_C+= opendir_test
+ATF_TESTS_C+= popen_test
+ATF_TESTS_C+= posix_spawn_test
+ATF_TESTS_C+= realpath2_test
+ATF_TESTS_C+= scandir_test
+.if ${COMPILER_FEATURES:Mblocks}
+ATF_TESTS_C+= scandir_blocks_test
+.endif
+ATF_TESTS_C+= sig2str_test
+ATF_TESTS_C+= sigsetops_test
+ATF_TESTS_C+= wordexp_test
+
+# TODO: t_closefrom, t_fmtcheck, t_randomid,
+# TODO: t_siginfo (fixes require further inspection)
+# TODO: t_sethostname_test (consistently screws up the hostname)
+
+FILESGROUPS+= posix_spawn_test_FILES
+
+posix_spawn_test_FILES= spawnp_enoexec.sh
+posix_spawn_test_FILESDIR= ${TESTSDIR}
+posix_spawn_test_FILESMODE= 0755
+posix_spawn_test_FILESOWN= root
+posix_spawn_test_FILESGRP= wheel
+posix_spawn_test_FILESPACKAGE= ${PACKAGE}
+
+CFLAGS+= -DTEST_LONG_DOUBLE
+
+# Define __HAVE_LONG_DOUBLE for architectures whose long double has greater
+# precision than their double.
+.if ${MACHINE_CPUARCH} == "aarch64" || \
+ ${MACHINE_CPUARCH} == "amd64" || \
+ ${MACHINE_CPUARCH} == "i386" || \
+ ${MACHINE_CPUARCH} == "riscv"
+CFLAGS+= -D__HAVE_LONG_DOUBLE
+.endif
+
+NETBSD_ATF_TESTS_C= alarm_test
+NETBSD_ATF_TESTS_C+= assert_test
+NETBSD_ATF_TESTS_C+= basedirname_test
+NETBSD_ATF_TESTS_C+= cpuset_test
+NETBSD_ATF_TESTS_C+= dir_test
+NETBSD_ATF_TESTS_C+= floatunditf_test
+NETBSD_ATF_TESTS_C+= fnmatch_test
+NETBSD_ATF_TESTS_C+= fpclassify_test
+NETBSD_ATF_TESTS_C+= fpsetmask_test
+NETBSD_ATF_TESTS_C+= fpsetround_test
+NETBSD_ATF_TESTS_C+= ftok_test
+NETBSD_ATF_TESTS_C+= getcwd_test
+NETBSD_ATF_TESTS_C+= getgrent_test
+NETBSD_ATF_TESTS_C+= glob_test
+NETBSD_ATF_TESTS_C+= humanize_number_test
+NETBSD_ATF_TESTS_C+= isnan_test
+NETBSD_ATF_TESTS_C+= nice_test
+NETBSD_ATF_TESTS_C+= pause_test
+NETBSD_ATF_TESTS_C+= raise_test
+NETBSD_ATF_TESTS_C+= realpath_test
+NETBSD_ATF_TESTS_C+= setdomainname_test
+NETBSD_ATF_TESTS_C+= sethostname_test
+NETBSD_ATF_TESTS_C+= sleep_test
+NETBSD_ATF_TESTS_C+= syslog_test
+NETBSD_ATF_TESTS_C+= time_test
+NETBSD_ATF_TESTS_C+= ttyname_test
+NETBSD_ATF_TESTS_C+= vis_test
+
+.include "../Makefile.netbsd-tests"
+
+CFLAGS.getentropy_test+= -I${SRCTOP}/include
+LIBADD.getentropy_test+= c
+LIBADD.humanize_number_test+= util
+
+LIBADD.fpclassify_test+=m
+LIBADD.fpsetround_test+=m
+LIBADD.siginfo_test+= m
+
+LIBADD.nice_test+= pthread
+LIBADD.syslog_test+= pthread
+
+CFLAGS+= -I${.CURDIR}
+
+SRCS.fmtcheck2_test= fmtcheck_test.c
+SRCS.fnmatch2_test= fnmatch_test.c
+
+TEST_METADATA.setdomainname_test+= is_exclusive=true
+TESTS_SUBDIRS= execve
+TESTS_SUBDIRS+= posix_spawn
+
+# Tests that require address sanitizer
+.if ${COMPILER_FEATURES:Masan}
+.for t in scandir_test realpath2_test
+CFLAGS.${t}.c+= -fsanitize=address
+LDFLAGS.${t}+= -fsanitize=address
+.endfor
+.endif
+
+# Tests that require blocks support
+.for t in fts_blocks_test glob_blocks_test scandir_blocks_test
+CFLAGS.${t}.c+= -fblocks
+LIBADD.${t}+= BlocksRuntime
+.endfor
+
+# The old testcase name
+TEST_FNMATCH= test-fnmatch
+CLEANFILES+= ${GEN_SH_CASE_TESTCASES}
+sh-tests: .PHONY
+.for target in clean obj depend all
+ @cd ${.CURDIR} && ${MAKE} PROG=${TEST_FNMATCH} \
+ -DNO_SUBDIR ${target}
+.endfor
+ @cd ${.OBJDIR} && ./${TEST_FNMATCH} -s 1 > \
+ ${SRCTOP}/bin/sh/tests/builtins/case2.0
+ @cd ${.OBJDIR} && ./${TEST_FNMATCH} -s 2 > \
+ ${SRCTOP}/bin/sh/tests/builtins/case3.0
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/gen/Makefile.depend b/lib/libc/tests/gen/Makefile.depend
new file mode 100644
index 000000000000..33d10e940cd0
--- /dev/null
+++ b/lib/libc/tests/gen/Makefile.depend
@@ -0,0 +1,21 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libnetbsd \
+ lib/libthr \
+ lib/libutil \
+ lib/msun \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/gen/arc4random_test.c b/lib/libc/tests/gen/arc4random_test.c
new file mode 100644
index 000000000000..79139f29f8fa
--- /dev/null
+++ b/lib/libc/tests/gen/arc4random_test.c
@@ -0,0 +1,112 @@
+/*-
+ * Copyright (c) 2011 David Schultz
+ * Copyright (c) 2024 Robert Clausecker <fuz@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+/*
+ * BUFSIZE is the number of bytes of rc4 output to compare. The probability
+ * that this test fails spuriously is 2**(-BUFSIZE * 8).
+ */
+#define BUFSIZE 8
+
+/*
+ * Test whether arc4random_buf() returns the same sequence of bytes in both
+ * parent and child processes. (Hint: It shouldn't.)
+ */
+ATF_TC_WITHOUT_HEAD(test_arc4random);
+ATF_TC_BODY(test_arc4random, tc)
+{
+ struct shared_page {
+ char parentbuf[BUFSIZE];
+ char childbuf[BUFSIZE];
+ } *page;
+ pid_t pid;
+ char c;
+
+ page = mmap(NULL, sizeof(struct shared_page), PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_SHARED, -1, 0);
+ ATF_REQUIRE_MSG(page != MAP_FAILED, "mmap failed; errno=%d", errno);
+
+ arc4random_buf(&c, 1);
+
+ pid = fork();
+ ATF_REQUIRE(0 <= pid);
+ if (pid == 0) {
+ /* child */
+ arc4random_buf(page->childbuf, BUFSIZE);
+ exit(0);
+ } else {
+ /* parent */
+ int status;
+ arc4random_buf(page->parentbuf, BUFSIZE);
+ wait(&status);
+ }
+ ATF_CHECK_MSG(memcmp(page->parentbuf, page->childbuf, BUFSIZE) != 0,
+ "sequences are the same");
+}
+
+/*
+ * Test whether arc4random_uniform() returns a number below the given threshold.
+ * Test with various thresholds.
+ */
+ATF_TC_WITHOUT_HEAD(test_arc4random_uniform);
+ATF_TC_BODY(test_arc4random_uniform, tc)
+{
+ size_t i, j;
+ static const uint32_t thresholds[] = {
+ 1, 2, 3, 4, 5, 10, 100, 1000,
+ INT32_MAX, (uint32_t)INT32_MAX + 1,
+ UINT32_MAX - 1000000000, UINT32_MAX - 1000000, UINT32_MAX - 1, 0
+ };
+
+ for (i = 0; thresholds[i] != 0; i++)
+ for (j = 0; j < 10000; j++)
+ ATF_CHECK(arc4random_uniform(thresholds[i]) < thresholds[i]);
+
+ /* for a threshold of zero, just check that we get zero every time */
+ for (j = 0; j < 1000; j++)
+ ATF_CHECK_EQ(0, arc4random_uniform(0));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, test_arc4random);
+ ATF_TP_ADD_TC(tp, test_arc4random_uniform);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/dir2_test.c b/lib/libc/tests/gen/dir2_test.c
new file mode 100644
index 000000000000..4ec5a1759d06
--- /dev/null
+++ b/lib/libc/tests/gen/dir2_test.c
@@ -0,0 +1,186 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2017 Spectra Logic Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test cases for operations on DIR objects:
+ * opendir, readdir, seekdir, telldir, closedir, etc
+ */
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+ATF_TC(telldir_after_seekdir);
+ATF_TC_HEAD(telldir_after_seekdir, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Calling telldir(3) after seekdir(3) "
+ "should return the argument passed to seekdir.");
+}
+ATF_TC_BODY(telldir_after_seekdir, tc)
+{
+ const int NUMFILES = 1000;
+ char template[] = "dXXXXXX";
+ char *tmpdir;
+ int i, dirfd;
+ DIR *dirp;
+ struct dirent *de;
+ long beginning, middle, end, td;
+
+ /* Create a temporary directory */
+ tmpdir = mkdtemp(template);
+ ATF_REQUIRE_MSG(tmpdir != NULL, "mkdtemp failed");
+ dirfd = open(tmpdir, O_RDONLY | O_DIRECTORY);
+ ATF_REQUIRE(dirfd > 0);
+
+ /*
+ * Fill it with files. Must be > 128 to ensure that the directory
+ * can't fit within a single page
+ */
+ for (i = 0; i < NUMFILES; i = i+1) {
+ int fd;
+ char filename[16];
+
+ snprintf(filename, sizeof(filename), "%d", i);
+ fd = openat(dirfd, filename, O_WRONLY | O_CREAT, 0600);
+ ATF_REQUIRE(fd > 0);
+ close(fd);
+ }
+
+ /* Get some directory bookmarks in various locations */
+ dirp = fdopendir(dirfd);
+ ATF_REQUIRE_MSG(dirfd >= 0, "fdopendir failed");
+ beginning = telldir(dirp);
+ for (i = 0; i < NUMFILES / 2; i = i+1) {
+ de = readdir(dirp);
+ ATF_REQUIRE_MSG(de != NULL, "readdir failed");
+ }
+ middle = telldir(dirp);
+ for (; i < NUMFILES - 1; i = i+1) {
+ de = readdir(dirp);
+ ATF_REQUIRE_MSG(de != NULL, "readdir failed");
+ }
+ end = telldir(dirp);
+
+ /*
+ * Seekdir to each bookmark, check the telldir after seekdir condition,
+ * and check that the bookmark is valid by reading another directory
+ * entry.
+ */
+
+ seekdir(dirp, beginning);
+ td = telldir(dirp);
+ ATF_CHECK_EQ(beginning, td);
+ ATF_REQUIRE_MSG(NULL != readdir(dirp), "invalid directory index");
+
+ seekdir(dirp, middle);
+ td = telldir(dirp);
+ ATF_CHECK_EQ(middle, td);
+ ATF_REQUIRE_MSG(NULL != readdir(dirp), "invalid directory index");
+
+ seekdir(dirp, end);
+ td = telldir(dirp);
+ ATF_CHECK_EQ(end, td);
+ ATF_REQUIRE_MSG(NULL != readdir(dirp), "invalid directory index");
+
+ closedir(dirp);
+}
+
+ATF_TC(telldir_at_end_of_block);
+ATF_TC_HEAD(telldir_at_end_of_block, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Calling telldir(3) after readdir(3) read the last entry in the block should return a valid location");
+}
+ATF_TC_BODY(telldir_at_end_of_block, tc)
+{
+ /* For UFS and ZFS, blocks roll over at 128 directory entries. */
+ const int NUMFILES = 129;
+ char template[] = "dXXXXXX";
+ char *tmpdir;
+ int i, dirfd;
+ DIR *dirp;
+ struct dirent *de;
+ long td;
+ char last_filename[16];
+
+ /* Create a temporary directory */
+ tmpdir = mkdtemp(template);
+ ATF_REQUIRE_MSG(tmpdir != NULL, "mkdtemp failed");
+ dirfd = open(tmpdir, O_RDONLY | O_DIRECTORY);
+ ATF_REQUIRE(dirfd > 0);
+
+ /*
+ * Fill it with files. Must be > 128 to ensure that the directory
+ * can't fit within a single page. The "-2" accounts for "." and ".."
+ */
+ for (i = 0; i < NUMFILES - 2; i = i+1) {
+ int fd;
+ char filename[16];
+
+ snprintf(filename, sizeof(filename), "%d", i);
+ fd = openat(dirfd, filename, O_WRONLY | O_CREAT, 0600);
+ ATF_REQUIRE(fd > 0);
+ close(fd);
+ }
+
+ /* Read all entries within the first page */
+ dirp = fdopendir(dirfd);
+ ATF_REQUIRE_MSG(dirfd >= 0, "fdopendir failed");
+ for (i = 0; i < NUMFILES - 1; i = i + 1)
+ ATF_REQUIRE_MSG(readdir(dirp) != NULL, "readdir failed");
+
+ /* Call telldir at the end of a page */
+ td = telldir(dirp);
+
+ /* Read the last entry */
+ de = readdir(dirp);
+ ATF_REQUIRE_MSG(de != NULL, "readdir failed");
+ strlcpy(last_filename, de->d_name, sizeof(last_filename));
+
+ /* Seek back to the bookmark. readdir() should return the last entry */
+ seekdir(dirp, td);
+ de = readdir(dirp);
+ ATF_REQUIRE_STREQ_MSG(last_filename, de->d_name,
+ "seekdir went to the wrong directory position");
+
+ closedir(dirp);
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, telldir_after_seekdir);
+ ATF_TP_ADD_TC(tp, telldir_at_end_of_block);
+
+ return atf_no_error();
+}
diff --git a/lib/libc/tests/gen/dlopen_empty_test.c b/lib/libc/tests/gen/dlopen_empty_test.c
new file mode 100644
index 000000000000..6fb3bf8d8343
--- /dev/null
+++ b/lib/libc/tests/gen/dlopen_empty_test.c
@@ -0,0 +1,94 @@
+/*-
+ * Copyright (c) 2016 Maksym Sobolyev
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/stat.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+static const char *funname;
+static char *soname;
+
+static void
+sigsegv_handler(int sig __unused)
+{
+ unlink(soname);
+ free(soname);
+ atf_tc_fail("got SIGSEGV in the %s(3)", funname);
+}
+
+ATF_TC(dlopen_empty_test);
+ATF_TC_HEAD(dlopen_empty_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the dlopen() of an empty file "
+ "returns an error");
+}
+ATF_TC_BODY(dlopen_empty_test, tc)
+{
+ char tempname[] = "/tmp/temp.XXXXXX";
+ char *fname;
+ int fd;
+ void *dlh;
+ struct sigaction act, oact;
+
+ fname = mktemp(tempname);
+ ATF_REQUIRE_MSG(fname != NULL, "mktemp failed; errno=%d", errno);
+ asprintf(&soname, "%s.so", fname);
+ ATF_REQUIRE_MSG(soname != NULL, "asprintf failed; errno=%d", ENOMEM);
+ fd = open(soname, O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE);
+ ATF_REQUIRE_MSG(fd != -1, "open(\"%s\") failed; errno=%d", soname, errno);
+ close(fd);
+
+ act.sa_handler = sigsegv_handler;
+ act.sa_flags = 0;
+ sigemptyset(&act.sa_mask);
+ ATF_CHECK_MSG(sigaction(SIGSEGV, &act, &oact) != -1,
+ "sigaction() failed");
+
+ funname = "dlopen";
+ dlh = dlopen(soname, RTLD_LAZY);
+ if (dlh != NULL) {
+ funname = "dlclose";
+ dlclose(dlh);
+ }
+ ATF_REQUIRE_MSG(dlh == NULL, "dlopen(\"%s\") did not fail", soname);
+ unlink(soname);
+ free(soname);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, dlopen_empty_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/execve/Makefile b/lib/libc/tests/gen/execve/Makefile
new file mode 100644
index 000000000000..7bf911d1cb47
--- /dev/null
+++ b/lib/libc/tests/gen/execve/Makefile
@@ -0,0 +1,7 @@
+.include <bsd.own.mk>
+
+NETBSD_ATF_TESTS_C= execve_test
+
+.include "../../Makefile.netbsd-tests"
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/gen/execve/Makefile.depend b/lib/libc/tests/gen/execve/Makefile.depend
new file mode 100644
index 000000000000..e89a5c52c82a
--- /dev/null
+++ b/lib/libc/tests/gen/execve/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libnetbsd \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/gen/fmtcheck_test.c b/lib/libc/tests/gen/fmtcheck_test.c
new file mode 100644
index 000000000000..27a12217e81e
--- /dev/null
+++ b/lib/libc/tests/gen/fmtcheck_test.c
@@ -0,0 +1,102 @@
+/* $NetBSD: tfmtcheck.c,v 1.3 2008/04/28 20:23:04 martin Exp $ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code was contributed to The NetBSD Foundation by Allen Briggs.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+struct test_fmt {
+ char *fmt1;
+ char *fmt2;
+ int correct;
+} test_fmts[] = {
+ { "%d", "%d", 1 },
+ { "%2d", "%2.2d", 1 },
+ { "%x", "%d", 1 },
+ { "%u", "%d", 1 },
+ { "%03d", "%d", 1 },
+ { "%-2d", "%d", 1 },
+ { "%d", "%-12.1d", 1 },
+ { "%d", "%-01.3d", 1 },
+ { "%X", "%-01.3d", 1 },
+ { "%D", "%ld", 1 },
+ { "%s", "%s", 1 },
+ { "%s", "This is a %s test", 1 },
+ { "Hi, there. This is a %s test", "%s", 1 },
+ { "%d", "%s", 2 },
+ { "%e", "%s", 2 },
+ { "%r", "%d", 2 },
+ { "%*.2d", "%*d", 1 },
+ { "%2.*d", "%*d", 2 },
+ { "%*d", "%*d", 1 },
+ { "%-3", "%d", 2 },
+ { "%d %s", "%d", 2 },
+ { "%*.*.*d", "%*.*.*d", 2 },
+ { "%d", "%d %s", 1 },
+ { "%40s", "%20s", 1 },
+ { "%x %x %x", "%o %u %d", 1 },
+ { "%o %u %d", "%x %x %X", 1 },
+ { "%#o %u %#-d", "%x %#x %X", 1 },
+ { "%qd", "%llx", 1 },
+ { "%%", "%llx", 1 },
+ { "%p %30s %#llx %-10.*e", "This number %lu%% and string %s has %qd numbers and %.*g floats", 1 },
+};
+
+ATF_TC_WITHOUT_HEAD(fmtcheck_test);
+ATF_TC_BODY(fmtcheck_test, tc)
+{
+ int i;
+ const char *f, *cf, *f1, *f2;
+
+ for (i = 0; i < nitems(test_fmts); i++) {
+ f1 = test_fmts[i].fmt1;
+ f2 = test_fmts[i].fmt2;
+ f = fmtcheck(f1, f2);
+ if (test_fmts[i].correct == 1)
+ cf = f1;
+ else
+ cf = f2;
+ ATF_CHECK_MSG(f == cf,
+ "Test %d: (%s) vs. (%s) failed "
+ "(should have returned %s)", i + 1, f1, f2,
+ (test_fmts[i].correct == 1) ? "1st" : "2nd");
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, fmtcheck_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/fmtmsg_test.c b/lib/libc/tests/gen/fmtmsg_test.c
new file mode 100644
index 000000000000..30a5156cdcc8
--- /dev/null
+++ b/lib/libc/tests/gen/fmtmsg_test.c
@@ -0,0 +1,249 @@
+/*-
+ * Copyright (c) 2012 Jilles Tjoelker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <err.h>
+#include <errno.h>
+#include <fmtmsg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+static char *run_test(long classification, const char *label, int severity,
+ const char *text, const char *action, const char *tag);
+
+struct testcase {
+ long classification;
+ const char *label;
+ int severity;
+ const char *text;
+ const char *action;
+ const char *tag;
+ const char *msgverb;
+ const char *result;
+} testcases[] = {
+ {
+ MM_UTIL | MM_PRINT, "BSD:ls", MM_ERROR,
+ "illegal option -- z", "refer to manual", "BSD:ls:001",
+ NULL,
+ "BSD:ls: ERROR: illegal option -- z\n"
+ "TO FIX: refer to manual BSD:ls:001\n"
+ },
+ {
+ MM_UTIL | MM_PRINT, "BSD:ls", MM_ERROR,
+ "illegal option -- z", "refer to manual", "BSD:ls:001",
+ "text:severity:action:tag",
+ "illegal option -- z: ERROR\n"
+ "TO FIX: refer to manual BSD:ls:001\n"
+ },
+ {
+ MM_UTIL | MM_PRINT, "BSD:ls", MM_ERROR,
+ "illegal option -- z", "refer to manual", "BSD:ls:001",
+ "text",
+ "illegal option -- z\n"
+ },
+ {
+ MM_UTIL | MM_PRINT, "BSD:ls", MM_ERROR,
+ "illegal option -- z", "refer to manual", "BSD:ls:001",
+ "severity:text",
+ "ERROR: illegal option -- z\n"
+ },
+ {
+ MM_UTIL | MM_PRINT, "BSD:ls", MM_ERROR,
+ "illegal option -- z", "refer to manual", "BSD:ls:001",
+ "ignore me",
+ "BSD:ls: ERROR: illegal option -- z\n"
+ "TO FIX: refer to manual BSD:ls:001\n"
+ },
+ {
+ MM_UTIL | MM_PRINT, "BSD:ls", MM_ERROR,
+ "illegal option -- z", "refer to manual", "BSD:ls:001",
+ "tag:severity:text:nothing:action",
+ "BSD:ls: ERROR: illegal option -- z\n"
+ "TO FIX: refer to manual BSD:ls:001\n"
+ },
+ {
+ MM_UTIL | MM_PRINT, "BSD:ls", MM_ERROR,
+ "illegal option -- z", "refer to manual", "BSD:ls:001",
+ "",
+ "BSD:ls: ERROR: illegal option -- z\n"
+ "TO FIX: refer to manual BSD:ls:001\n"
+ },
+ {
+ MM_UTIL | MM_PRINT, MM_NULLLBL, MM_ERROR,
+ "illegal option -- z", "refer to manual", "BSD:ls:001",
+ NULL,
+ "ERROR: illegal option -- z\n"
+ "TO FIX: refer to manual BSD:ls:001\n"
+ },
+ {
+ MM_UTIL | MM_PRINT, "BSD:ls", MM_ERROR,
+ "illegal option -- z", MM_NULLACT, MM_NULLTAG,
+ NULL,
+ "BSD:ls: ERROR: illegal option -- z\n"
+ },
+ {
+ MM_UTIL | MM_NULLMC, "BSD:ls", MM_ERROR,
+ "illegal option -- z", "refer to manual", "BSD:ls:001",
+ NULL,
+ ""
+ },
+ {
+ MM_APPL | MM_PRINT, "ABCDEFGHIJ:abcdefghijklmn", MM_INFO,
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+ "refer to manual", "ABCDEFGHIJ:abcdefghijklmn:001",
+ NULL,
+ "ABCDEFGHIJ:abcdefghijklmn: INFO: "
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n"
+ "TO FIX: refer to manual ABCDEFGHIJ:abcdefghijklmn:001\n"
+ },
+ {
+ MM_OPSYS | MM_PRINT, "TEST:test", MM_HALT,
+ "failed", "nothing can help me", "NOTHING",
+ NULL,
+ "TEST:test: HALT: failed\n"
+ "TO FIX: nothing can help me NOTHING\n"
+ },
+ {
+ MM_OPSYS | MM_PRINT, "TEST:test", MM_WARNING,
+ "failed", "nothing can help me", "NOTHING",
+ NULL,
+ "TEST:test: WARNING: failed\n"
+ "TO FIX: nothing can help me NOTHING\n"
+ },
+ {
+ MM_OPSYS | MM_PRINT, "TEST:test", MM_NOSEV,
+ "failed", "nothing can help me", "NOTHING",
+ NULL,
+ "TEST:test: failed\n"
+ "TO FIX: nothing can help me NOTHING\n"
+ }
+};
+
+static char *
+run_test(long classification, const char *label, int severity,
+ const char *text, const char *action, const char *tag)
+{
+ int pip[2];
+ pid_t pid, wpid;
+ char *result, *p;
+ size_t resultsize;
+ ssize_t n;
+ int status;
+
+ if (pipe(pip) == -1)
+ err(2, "pipe");
+ pid = fork();
+ if (pid == -1)
+ err(2, "fork");
+ if (pid == 0) {
+ close(pip[0]);
+ if (pip[1] != STDERR_FILENO &&
+ dup2(pip[1], STDERR_FILENO) == -1)
+ _exit(2);
+ if (fmtmsg(classification, label, severity, text, action, tag)
+ != MM_OK)
+ _exit(1);
+ else
+ _exit(0);
+ }
+ close(pip[1]);
+ resultsize = 1024;
+ result = malloc(resultsize);
+ p = result;
+ while ((n = read(pip[0], p, result + resultsize - p - 1)) != 0) {
+ if (n == -1) {
+ if (errno == EINTR)
+ continue;
+ else
+ err(2, "read");
+ }
+ p += n;
+ if (result + resultsize == p - 1) {
+ resultsize *= 2;
+ result = realloc(result, resultsize);
+ if (result == NULL)
+ err(2, "realloc");
+ }
+ }
+ if (memchr(result, '\0', p - result) != NULL) {
+ free(result);
+ return (NULL);
+ }
+ *p = '\0';
+ close(pip[0]);
+ while ((wpid = waitpid(pid, &status, 0)) == -1 && errno == EINTR)
+ ;
+ if (wpid == -1)
+ err(2, "waitpid");
+ if (status != 0) {
+ free(result);
+ return (NULL);
+ }
+ return (result);
+}
+
+ATF_TC_WITHOUT_HEAD(fmtmsg_test);
+ATF_TC_BODY(fmtmsg_test, tc)
+{
+ char *result;
+ struct testcase *t;
+ int i;
+
+ for (i = 0; i < nitems(testcases); i++) {
+ t = &testcases[i];
+ if (t->msgverb != NULL)
+ setenv("MSGVERB", t->msgverb, 1);
+ else
+ unsetenv("MSGVERB");
+ result = run_test(t->classification, t->label, t->severity,
+ t->text, t->action, t->tag);
+ ATF_CHECK_MSG(result != NULL, "testcase %d failed", i + 1);
+ if (result != NULL)
+ ATF_CHECK_MSG(strcmp(result, t->result) == 0,
+ "results for testcase %d didn't match; "
+ "`%s` != `%s`", i + 1, result, t->result);
+ free(result);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, fmtmsg_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/fnmatch_test.c b/lib/libc/tests/gen/fnmatch_test.c
new file mode 100644
index 000000000000..0ff7400a4a4f
--- /dev/null
+++ b/lib/libc/tests/gen/fnmatch_test.c
@@ -0,0 +1,266 @@
+/*-
+ * Copyright (c) 2010 Jilles Tjoelker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <errno.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "fnmatch_testcases.h"
+
+static const char *
+flags_to_string(int flags)
+{
+ static const int flagvalues[] = { FNM_NOESCAPE, FNM_PATHNAME,
+ FNM_PERIOD, FNM_LEADING_DIR, FNM_CASEFOLD, 0 };
+ static const char flagnames[] = "FNM_NOESCAPE\0FNM_PATHNAME\0FNM_PERIOD\0FNM_LEADING_DIR\0FNM_CASEFOLD\0";
+ static char result[sizeof(flagnames) + 3 * sizeof(int) + 2];
+ char *p;
+ size_t i, len;
+ const char *fp;
+
+ p = result;
+ fp = flagnames;
+ for (i = 0; flagvalues[i] != 0; i++) {
+ len = strlen(fp);
+ if (flags & flagvalues[i]) {
+ if (p != result)
+ *p++ = '|';
+ memcpy(p, fp, len);
+ p += len;
+ flags &= ~flagvalues[i];
+ }
+ fp += len + 1;
+ }
+ if (p == result)
+ memcpy(p, "0", 2);
+ else if (flags != 0)
+ sprintf(p, "%d", flags);
+ else
+ *p = '\0';
+ return result;
+}
+
+ATF_TC_WITHOUT_HEAD(fnmatch_test);
+ATF_TC_BODY(fnmatch_test, tc)
+{
+ size_t i;
+ int flags, result;
+ struct testcase *t;
+
+ for (i = 0; i < nitems(testcases); i++) {
+ t = &testcases[i];
+ flags = t->flags;
+ do {
+ result = fnmatch(t->pattern, t->string, flags);
+ if (result != t->result)
+ break;
+ if (strchr(t->pattern, '\\') == NULL &&
+ !(flags & FNM_NOESCAPE)) {
+ flags |= FNM_NOESCAPE;
+ result = fnmatch(t->pattern, t->string, flags);
+ if (result != t->result)
+ break;
+ flags = t->flags;
+ }
+ if (strchr(t->pattern, '\\') != NULL &&
+ strchr(t->string, '\\') == NULL &&
+ t->result == FNM_NOMATCH &&
+ !(flags & (FNM_NOESCAPE | FNM_LEADING_DIR))) {
+ flags |= FNM_NOESCAPE;
+ result = fnmatch(t->pattern, t->string, flags);
+ if (result != t->result)
+ break;
+ flags = t->flags;
+ }
+ if ((t->string[0] != '.' || t->pattern[0] == '.' ||
+ t->result == FNM_NOMATCH) &&
+ !(flags & (FNM_PATHNAME | FNM_PERIOD))) {
+ flags |= FNM_PERIOD;
+ result = fnmatch(t->pattern, t->string, flags);
+ if (result != t->result)
+ break;
+ flags = t->flags;
+ }
+ if ((strchr(t->string, '/') == NULL ||
+ t->result == FNM_NOMATCH) &&
+ !(flags & FNM_PATHNAME)) {
+ flags |= FNM_PATHNAME;
+ result = fnmatch(t->pattern, t->string, flags);
+ if (result != t->result)
+ break;
+ flags = t->flags;
+ }
+ if ((((t->string[0] != '.' || t->pattern[0] == '.') &&
+ strstr(t->string, "/.") == NULL) ||
+ t->result == FNM_NOMATCH) &&
+ flags & FNM_PATHNAME && !(flags & FNM_PERIOD)) {
+ flags |= FNM_PERIOD;
+ result = fnmatch(t->pattern, t->string, flags);
+ if (result != t->result)
+ break;
+ flags = t->flags;
+ }
+ if ((((t->string[0] != '.' || t->pattern[0] == '.') &&
+ strchr(t->string, '/') == NULL) ||
+ t->result == FNM_NOMATCH) &&
+ !(flags & (FNM_PATHNAME | FNM_PERIOD))) {
+ flags |= FNM_PATHNAME | FNM_PERIOD;
+ result = fnmatch(t->pattern, t->string, flags);
+ if (result != t->result)
+ break;
+ flags = t->flags;
+ }
+ if ((strchr(t->string, '/') == NULL || t->result == 0)
+ && !(flags & FNM_LEADING_DIR)) {
+ flags |= FNM_LEADING_DIR;
+ result = fnmatch(t->pattern, t->string, flags);
+ if (result != t->result)
+ break;
+ flags = t->flags;
+ }
+ if (t->result == 0 && !(flags & FNM_CASEFOLD)) {
+ flags |= FNM_CASEFOLD;
+ result = fnmatch(t->pattern, t->string, flags);
+ if (result != t->result)
+ break;
+ flags = t->flags;
+ }
+ if (strchr(t->pattern, '\\') == NULL &&
+ t->result == 0 &&
+ !(flags & (FNM_NOESCAPE | FNM_CASEFOLD))) {
+ flags |= FNM_NOESCAPE | FNM_CASEFOLD;
+ result = fnmatch(t->pattern, t->string, flags);
+ if (result != t->result)
+ break;
+ flags = t->flags;
+ }
+ } while (0);
+
+ ATF_CHECK(result == t->result);
+ if (result == t->result)
+ printf("fnmatch(\"%s\", \"%s\", %s) == %d\n",
+ t->pattern, t->string, flags_to_string(flags), result);
+ else
+ printf("fnmatch(\"%s\", \"%s\", %s) != %d (was %d)\n",
+ t->pattern, t->string, flags_to_string(flags),
+ t->result, result);
+ }
+
+}
+
+ATF_TC(fnmatch_characterclass);
+ATF_TC_HEAD(fnmatch_characterclass, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test fnmatch with character classes");
+}
+
+ATF_TC_BODY(fnmatch_characterclass, tc)
+{
+ ATF_CHECK(fnmatch("[[:alnum:]]", "a", 0) == 0);
+ ATF_CHECK(fnmatch("[[:cntrl:]]", "\a", 0) == 0);
+ ATF_CHECK(fnmatch("[[:lower:]]", "a", 0) == 0);
+ ATF_CHECK(fnmatch("[[:space:]]", " ", 0) == 0);
+ ATF_CHECK(fnmatch("[[:alpha:]]", "a", 0) == 0);
+ ATF_CHECK(fnmatch("[[:digit:]]", "0", 0) == 0);
+ ATF_CHECK(fnmatch("[[:print:]]", "a", 0) == 0);
+ ATF_CHECK(fnmatch("[[:upper:]]", "A", 0) == 0);
+ ATF_CHECK(fnmatch("[[:blank:]]", " ", 0) == 0);
+ ATF_CHECK(fnmatch("[[:graph:]]", "a", 0) == 0);
+ ATF_CHECK(fnmatch("[[:punct:]]", ".", 0) == 0);
+ ATF_CHECK(fnmatch("[[:xdigit:]]", "f", 0) == 0);
+
+ /*
+ * POSIX.1, section 9.3.5. states that '[:' and ':]'
+ * should be interpreted as character classes symbol only
+ * when part of a bracket expression.
+ */
+ ATF_CHECK(fnmatch("[:alnum:]", "a", 0) == 0);
+ ATF_CHECK(fnmatch("[:alnum:]", ":", 0) == 0);
+ ATF_CHECK(fnmatch("[:alnum:]", "1", 0) != 0);
+}
+
+ATF_TC(fnmatch_collsym);
+ATF_TC_HEAD(fnmatch_collsym, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test fnmatch with collating symbols");
+}
+
+ATF_TC_BODY(fnmatch_collsym, tc)
+{
+ setlocale(LC_ALL, "cs_CZ.UTF-8");
+ ATF_CHECK(fnmatch("[ch]", "ch", 0) != 0);
+ ATF_CHECK(fnmatch("[[.ch.]]", "ch", 0) == 0);
+ ATF_CHECK(fnmatch("[[.ch.]]h", "chh", 0) == 0);
+
+ /*
+ * POSIX.1, section 9.3.5. states that '[.' and '.]'
+ * should be interpreted as a collating symbol only
+ * when part of a bracket expression.
+ */
+ ATF_CHECK(fnmatch("[.ch.]", "c", 0) == 0);
+ ATF_CHECK(fnmatch("[.ch.]", "h", 0) == 0);
+ ATF_CHECK(fnmatch("[.ch.]", ".", 0) == 0);
+}
+
+ATF_TC(fnmatch_equivclass);
+ATF_TC_HEAD(fnmatch_equivclass, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test fnmatch with equivalence classes");
+}
+
+ATF_TC_BODY(fnmatch_equivclass, tc)
+{
+ setlocale(LC_ALL, "en_US.UTF-8");
+ ATF_CHECK(fnmatch("[[=a=]]b", "ab", 0) == 0);
+ ATF_CHECK(fnmatch("[[=a=]]b", "Ab", 0) == 0);
+ ATF_CHECK(fnmatch("[[=à=]]b", "ab", 0) == 0);
+ ATF_CHECK(fnmatch("[[=a=]]b", "àb", 0) == 0);
+
+ /*
+ * POSIX.1, section 9.3.5. states that '[=' and '=]'
+ * should be interpreted as an equivalence class only
+ * when part of a bracket expression.
+ */
+ ATF_CHECK(fnmatch("[=a=]b", "=b", 0) == 0);
+ ATF_CHECK(fnmatch("[=a=]b", "ab", 0) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, fnmatch_test);
+ ATF_TP_ADD_TC(tp, fnmatch_collsym);
+ ATF_TP_ADD_TC(tp, fnmatch_characterclass);
+ ATF_TP_ADD_TC(tp, fnmatch_equivclass);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/fnmatch_testcases.h b/lib/libc/tests/gen/fnmatch_testcases.h
new file mode 100644
index 000000000000..196160a4801b
--- /dev/null
+++ b/lib/libc/tests/gen/fnmatch_testcases.h
@@ -0,0 +1,173 @@
+/*-
+ * Copyright (c) 2010 Jilles Tjoelker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <fnmatch.h>
+
+struct testcase {
+ const char *pattern;
+ const char *string;
+ int flags;
+ int result;
+} testcases[] = {
+ { "", "", 0, 0 },
+ { "a", "a", 0, 0 },
+ { "a", "b", 0, FNM_NOMATCH },
+ { "a", "A", 0, FNM_NOMATCH },
+ { "*", "a", 0, 0 },
+ { "*", "aa", 0, 0 },
+ { "*a", "a", 0, 0 },
+ { "*a", "b", 0, FNM_NOMATCH },
+ { "*a*", "b", 0, FNM_NOMATCH },
+ { "*a*b*", "ab", 0, 0 },
+ { "*a*b*", "qaqbq", 0, 0 },
+ { "*a*bb*", "qaqbqbbq", 0, 0 },
+ { "*a*bc*", "qaqbqbcq", 0, 0 },
+ { "*a*bb*", "qaqbqbb", 0, 0 },
+ { "*a*bc*", "qaqbqbc", 0, 0 },
+ { "*a*bb", "qaqbqbb", 0, 0 },
+ { "*a*bc", "qaqbqbc", 0, 0 },
+ { "*a*bb", "qaqbqbbq", 0, FNM_NOMATCH },
+ { "*a*bc", "qaqbqbcq", 0, FNM_NOMATCH },
+ { "*a*a*a*a*a*a*a*a*a*a*", "aaaaaaaaa", 0, FNM_NOMATCH },
+ { "*a*a*a*a*a*a*a*a*a*a*", "aaaaaaaaaa", 0, 0 },
+ { "*a*a*a*a*a*a*a*a*a*a*", "aaaaaaaaaaa", 0, 0 },
+ { ".*.*.*.*.*.*.*.*.*.*", ".........", 0, FNM_NOMATCH },
+ { ".*.*.*.*.*.*.*.*.*.*", "..........", 0, 0 },
+ { ".*.*.*.*.*.*.*.*.*.*", "...........", 0, 0 },
+ { "*?*?*?*?*?*?*?*?*?*?*", "123456789", 0, FNM_NOMATCH },
+ { "??????????*", "123456789", 0, FNM_NOMATCH },
+ { "*??????????", "123456789", 0, FNM_NOMATCH },
+ { "*?*?*?*?*?*?*?*?*?*?*", "1234567890", 0, 0 },
+ { "??????????*", "1234567890", 0, 0 },
+ { "*??????????", "1234567890", 0, 0 },
+ { "*?*?*?*?*?*?*?*?*?*?*", "12345678901", 0, 0 },
+ { "??????????*", "12345678901", 0, 0 },
+ { "*??????????", "12345678901", 0, 0 },
+ { "[x]", "x", 0, 0 },
+ { "[*]", "*", 0, 0 },
+ { "[?]", "?", 0, 0 },
+ { "[", "[", 0, 0 },
+ { "[[]", "[", 0, 0 },
+ { "[[]", "x", 0, FNM_NOMATCH },
+ { "[*]", "", 0, FNM_NOMATCH },
+ { "[*]", "x", 0, FNM_NOMATCH },
+ { "[?]", "x", 0, FNM_NOMATCH },
+ { "*[*]*", "foo*foo", 0, 0 },
+ { "*[*]*", "foo", 0, FNM_NOMATCH },
+ { "[0-9]", "0", 0, 0 },
+ { "[0-9]", "5", 0, 0 },
+ { "[0-9]", "9", 0, 0 },
+ { "[0-9]", "/", 0, FNM_NOMATCH },
+ { "[0-9]", ":", 0, FNM_NOMATCH },
+ { "[0-9]", "*", 0, FNM_NOMATCH },
+ { "[!0-9]", "0", 0, FNM_NOMATCH },
+ { "[!0-9]", "5", 0, FNM_NOMATCH },
+ { "[!0-9]", "9", 0, FNM_NOMATCH },
+ { "[!0-9]", "/", 0, 0 },
+ { "[!0-9]", ":", 0, 0 },
+ { "[!0-9]", "*", 0, 0 },
+ { "*[0-9]", "a0", 0, 0 },
+ { "*[0-9]", "a5", 0, 0 },
+ { "*[0-9]", "a9", 0, 0 },
+ { "*[0-9]", "a/", 0, FNM_NOMATCH },
+ { "*[0-9]", "a:", 0, FNM_NOMATCH },
+ { "*[0-9]", "a*", 0, FNM_NOMATCH },
+ { "*[!0-9]", "a0", 0, FNM_NOMATCH },
+ { "*[!0-9]", "a5", 0, FNM_NOMATCH },
+ { "*[!0-9]", "a9", 0, FNM_NOMATCH },
+ { "*[!0-9]", "a/", 0, 0 },
+ { "*[!0-9]", "a:", 0, 0 },
+ { "*[!0-9]", "a*", 0, 0 },
+ { "*[0-9]", "a00", 0, 0 },
+ { "*[0-9]", "a55", 0, 0 },
+ { "*[0-9]", "a99", 0, 0 },
+ { "*[0-9]", "a0a0", 0, 0 },
+ { "*[0-9]", "a5a5", 0, 0 },
+ { "*[0-9]", "a9a9", 0, 0 },
+ { "\\*", "*", 0, 0 },
+ { "\\?", "?", 0, 0 },
+ { "\\[x]", "[x]", 0, 0 },
+ { "\\[", "[", 0, 0 },
+ { "\\\\", "\\", 0, 0 },
+ { "*\\**", "foo*foo", 0, 0 },
+ { "*\\**", "foo", 0, FNM_NOMATCH },
+ { "*\\\\*", "foo\\foo", 0, 0 },
+ { "*\\\\*", "foo", 0, FNM_NOMATCH },
+ { "\\(", "(", 0, 0 },
+ { "\\a", "a", 0, 0 },
+ { "\\*", "a", 0, FNM_NOMATCH },
+ { "\\?", "a", 0, FNM_NOMATCH },
+ { "\\*", "\\*", 0, FNM_NOMATCH },
+ { "\\?", "\\?", 0, FNM_NOMATCH },
+ { "\\[x]", "\\[x]", 0, FNM_NOMATCH },
+ { "\\[x]", "\\x", 0, FNM_NOMATCH },
+ { "\\[", "\\[", 0, FNM_NOMATCH },
+ { "\\(", "\\(", 0, FNM_NOMATCH },
+ { "\\a", "\\a", 0, FNM_NOMATCH },
+ { "\\", "\\", 0, FNM_NOMATCH },
+ { "\\", "", 0, FNM_NOMATCH },
+ { "\\*", "\\*", FNM_NOESCAPE, 0 },
+ { "\\?", "\\?", FNM_NOESCAPE, 0 },
+ { "\\", "\\", FNM_NOESCAPE, 0 },
+ { "\\\\", "\\", FNM_NOESCAPE, FNM_NOMATCH },
+ { "\\\\", "\\\\", FNM_NOESCAPE, 0 },
+ { "*\\*", "foo\\foo", FNM_NOESCAPE, 0 },
+ { "*\\*", "foo", FNM_NOESCAPE, FNM_NOMATCH },
+ { "*", ".", FNM_PERIOD, FNM_NOMATCH },
+ { "?", ".", FNM_PERIOD, FNM_NOMATCH },
+ { ".*", ".", 0, 0 },
+ { ".*", "..", 0, 0 },
+ { ".*", ".a", 0, 0 },
+ { "[0-9]", ".", FNM_PERIOD, FNM_NOMATCH },
+ { "a*", "a.", 0, 0 },
+ { "a/a", "a/a", FNM_PATHNAME, 0 },
+ { "a/*", "a/a", FNM_PATHNAME, 0 },
+ { "*/a", "a/a", FNM_PATHNAME, 0 },
+ { "*/*", "a/a", FNM_PATHNAME, 0 },
+ { "a*b/*", "abbb/x", FNM_PATHNAME, 0 },
+ { "a*b/*", "abbb/.x", FNM_PATHNAME, 0 },
+ { "*", "a/a", FNM_PATHNAME, FNM_NOMATCH },
+ { "*/*", "a/a/a", FNM_PATHNAME, FNM_NOMATCH },
+ { "b/*", "b/.x", FNM_PATHNAME | FNM_PERIOD, FNM_NOMATCH },
+ { "b*/*", "a/.x", FNM_PATHNAME | FNM_PERIOD, FNM_NOMATCH },
+ { "b/.*", "b/.x", FNM_PATHNAME | FNM_PERIOD, 0 },
+ { "b*/.*", "b/.x", FNM_PATHNAME | FNM_PERIOD, 0 },
+ { "a", "A", FNM_CASEFOLD, 0 },
+ { "A", "a", FNM_CASEFOLD, 0 },
+ { "[a]", "A", FNM_CASEFOLD, 0 },
+ { "[A]", "a", FNM_CASEFOLD, 0 },
+ { "a", "b", FNM_CASEFOLD, FNM_NOMATCH },
+ { "a", "a/b", FNM_PATHNAME, FNM_NOMATCH },
+ { "*", "a/b", FNM_PATHNAME, FNM_NOMATCH },
+ { "*b", "a/b", FNM_PATHNAME, FNM_NOMATCH },
+ { "a", "a/b", FNM_PATHNAME | FNM_LEADING_DIR, 0 },
+ { "*", "a/b", FNM_PATHNAME | FNM_LEADING_DIR, 0 },
+ { "*", ".a/b", FNM_PATHNAME | FNM_LEADING_DIR, 0 },
+ { "*a", ".a/b", FNM_PATHNAME | FNM_LEADING_DIR, 0 },
+ { "*", ".a/b", FNM_PATHNAME | FNM_PERIOD | FNM_LEADING_DIR, FNM_NOMATCH },
+ { "*a", ".a/b", FNM_PATHNAME | FNM_PERIOD | FNM_LEADING_DIR, FNM_NOMATCH },
+ { "a*b/*", "abbb/.x", FNM_PATHNAME | FNM_PERIOD, FNM_NOMATCH },
+};
diff --git a/lib/libc/tests/gen/fpclassify2_test.c b/lib/libc/tests/gen/fpclassify2_test.c
new file mode 100644
index 000000000000..45180ac5be42
--- /dev/null
+++ b/lib/libc/tests/gen/fpclassify2_test.c
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 2003 Mike Barcroft <mike@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(test_fpclassify);
+ATF_TC_BODY(test_fpclassify, tc)
+{
+
+ ATF_CHECK(fpclassify((float)0) == FP_ZERO);
+ ATF_CHECK(fpclassify((float)-0.0) == FP_ZERO);
+ ATF_CHECK(fpclassify((float)1) == FP_NORMAL);
+ ATF_CHECK(fpclassify((float)1000) == FP_NORMAL);
+ ATF_CHECK(fpclassify(HUGE_VALF) == FP_INFINITE);
+ ATF_CHECK(fpclassify((float)HUGE_VAL) == FP_INFINITE);
+ ATF_CHECK(fpclassify((float)HUGE_VALL) == FP_INFINITE);
+ ATF_CHECK(fpclassify(NAN) == FP_NAN);
+
+ ATF_CHECK(fpclassify((double)0) == FP_ZERO);
+ ATF_CHECK(fpclassify((double)-0) == FP_ZERO);
+ ATF_CHECK(fpclassify((double)1) == FP_NORMAL);
+ ATF_CHECK(fpclassify((double)1000) == FP_NORMAL);
+ ATF_CHECK(fpclassify(HUGE_VAL) == FP_INFINITE);
+ ATF_CHECK(fpclassify((double)HUGE_VALF) == FP_INFINITE);
+ ATF_CHECK(fpclassify((double)HUGE_VALL) == FP_INFINITE);
+ ATF_CHECK(fpclassify((double)NAN) == FP_NAN);
+
+ ATF_CHECK(fpclassify((long double)0) == FP_ZERO);
+ ATF_CHECK(fpclassify((long double)-0.0) == FP_ZERO);
+ ATF_CHECK(fpclassify((long double)1) == FP_NORMAL);
+ ATF_CHECK(fpclassify((long double)1000) == FP_NORMAL);
+ ATF_CHECK(fpclassify(HUGE_VALL) == FP_INFINITE);
+ ATF_CHECK(fpclassify((long double)HUGE_VALF) == FP_INFINITE);
+ ATF_CHECK(fpclassify((long double)HUGE_VAL) == FP_INFINITE);
+ ATF_CHECK(fpclassify((long double)NAN) == FP_NAN);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, test_fpclassify);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/fts_blocks_test.c b/lib/libc/tests/gen/fts_blocks_test.c
new file mode 100644
index 000000000000..f020dd8dea45
--- /dev/null
+++ b/lib/libc/tests/gen/fts_blocks_test.c
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <fts.h>
+
+#include <atf-c.h>
+
+/*
+ * Create two directories with three files each in lexicographical order,
+ * then call FTS with a sort block that sorts in reverse lexicographical
+ * order. This has the least chance of getting a false positive due to
+ * differing file system semantics. UFS will return the files in the
+ * order they were created while ZFS will sort them lexicographically; in
+ * both cases, the order we expect is the reverse.
+ */
+ATF_TC(fts_blocks_test);
+ATF_TC_HEAD(fts_blocks_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test FTS with a block in lieu of a comparison function");
+}
+ATF_TC_BODY(fts_blocks_test, tc)
+{
+ char *args[] = {
+ "bar", "foo", NULL
+ };
+ char *paths[] = {
+ "foo", "z", "y", "x", "foo",
+ "bar", "c", "b", "a", "bar",
+ NULL
+ };
+ char **expect = paths;
+ FTS *fts;
+ FTSENT *ftse;
+
+ ATF_REQUIRE_EQ(0, mkdir("bar", 0755));
+ ATF_REQUIRE_EQ(0, close(creat("bar/a", 0644)));
+ ATF_REQUIRE_EQ(0, close(creat("bar/b", 0644)));
+ ATF_REQUIRE_EQ(0, close(creat("bar/c", 0644)));
+ ATF_REQUIRE_EQ(0, mkdir("foo", 0755));
+ ATF_REQUIRE_EQ(0, close(creat("foo/x", 0644)));
+ ATF_REQUIRE_EQ(0, close(creat("foo/y", 0644)));
+ ATF_REQUIRE_EQ(0, close(creat("foo/z", 0644)));
+ fts = fts_open_b(args, 0,
+ ^(const FTSENT * const *a, const FTSENT * const *b) {
+ return (strcmp((*b)->fts_name, (*a)->fts_name));
+ });
+ ATF_REQUIRE_MSG(fts != NULL, "fts_open_b(): %m");
+ while ((ftse = fts_read(fts)) != NULL && *expect != NULL) {
+ ATF_CHECK_STREQ(*expect, ftse->fts_name);
+ expect++;
+ }
+ ATF_CHECK_EQ(NULL, ftse);
+ ATF_CHECK_EQ(NULL, *expect);
+ ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, fts_blocks_test);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/fts_misc_test.c b/lib/libc/tests/gen/fts_misc_test.c
new file mode 100644
index 000000000000..91640078f63c
--- /dev/null
+++ b/lib/libc/tests/gen/fts_misc_test.c
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <fts.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "fts_test.h"
+
+ATF_TC(fts_unrdir);
+ATF_TC_HEAD(fts_unrdir, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "unreadable directories");
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(fts_unrdir, tc)
+{
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE_EQ(0, mkdir("dir/unr", 0100));
+ ATF_REQUIRE_EQ(0, mkdir("dir/unx", 0400));
+ fts_test(tc, &(struct fts_testcase){
+ (char *[]){ "dir", NULL },
+ FTS_PHYSICAL,
+ (struct fts_expect[]){
+ { FTS_D, "dir", "dir" },
+ { FTS_D, "unr", "unr" },
+ { FTS_DNR, "unr", "unr" },
+ { FTS_D, "unx", "unx" },
+ { FTS_DP, "unx", "unx" },
+ { FTS_DP, "dir", "dir" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TC(fts_unrdir_nochdir);
+ATF_TC_HEAD(fts_unrdir_nochdir, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "unreadable directories (nochdir)");
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(fts_unrdir_nochdir, tc)
+{
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE_EQ(0, mkdir("dir/unr", 0100));
+ ATF_REQUIRE_EQ(0, mkdir("dir/unx", 0400));
+ fts_test(tc, &(struct fts_testcase){
+ (char *[]){ "dir", NULL },
+ FTS_PHYSICAL | FTS_NOCHDIR,
+ (struct fts_expect[]){
+ { FTS_D, "dir", "dir" },
+ { FTS_D, "unr", "dir/unr" },
+ { FTS_DNR, "unr", "dir/unr" },
+ { FTS_D, "unx", "dir/unx" },
+ { FTS_DP, "unx", "dir/unx" },
+ { FTS_DP, "dir", "dir" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ fts_check_debug();
+ ATF_TP_ADD_TC(tp, fts_unrdir);
+ ATF_TP_ADD_TC(tp, fts_unrdir_nochdir);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/fts_options_test.c b/lib/libc/tests/gen/fts_options_test.c
new file mode 100644
index 000000000000..fc3015138a49
--- /dev/null
+++ b/lib/libc/tests/gen/fts_options_test.c
@@ -0,0 +1,394 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <fts.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "fts_test.h"
+
+static char *all_paths[] = {
+ "dir",
+ "dirl",
+ "file",
+ "filel",
+ "dead",
+ "noent",
+ NULL
+};
+
+/*
+ * Prepare the files and directories we will be inspecting.
+ */
+static void
+fts_options_prepare(const struct atf_tc *tc)
+{
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE_EQ(0, close(creat("file", 0644)));
+ ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644)));
+ ATF_REQUIRE_EQ(0, symlink("..", "dir/up"));
+ ATF_REQUIRE_EQ(0, symlink("dir", "dirl"));
+ ATF_REQUIRE_EQ(0, symlink("file", "filel"));
+ ATF_REQUIRE_EQ(0, symlink("noent", "dead"));
+}
+
+ATF_TC(fts_options_logical);
+ATF_TC_HEAD(fts_options_logical, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_LOGICAL");
+}
+ATF_TC_BODY(fts_options_logical, tc)
+{
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_LOGICAL,
+ (struct fts_expect[]){
+ { FTS_DL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_F, "file", "dir/file" },
+ { FTS_D, "up", "dir/up" },
+ { FTS_DL, "dead", "dir/up/dead" },
+ { FTS_DC, "dir", "dir/up/dir" },
+ { FTS_DC, "dirl", "dir/up/dirl" },
+ { FTS_F, "file", "dir/up/file" },
+ { FTS_F, "filel", "dir/up/filel" },
+ { FTS_DP, "up", "dir/up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_D, "dirl", "dirl" },
+ { FTS_F, "file", "dirl/file" },
+ { FTS_D, "up", "dirl/up" },
+ { FTS_DL, "dead", "dirl/up/dead" },
+ { FTS_DC, "dir", "dirl/up/dir" },
+ { FTS_DC, "dirl", "dirl/up/dirl" },
+ { FTS_F, "file", "dirl/up/file" },
+ { FTS_F, "filel", "dirl/up/filel" },
+ { FTS_DP, "up", "dirl/up" },
+ { FTS_DP, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_F, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TC(fts_options_logical_nostat);
+ATF_TC_HEAD(fts_options_logical_nostat, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_LOGICAL | FTS_NOSTAT");
+}
+ATF_TC_BODY(fts_options_logical_nostat, tc)
+{
+ /*
+ * While FTS_LOGICAL is not documented as being incompatible with
+ * FTS_NOSTAT, and FTS does not clear FTS_NOSTAT if FTS_LOGICAL is
+ * set, FTS_LOGICAL effectively nullifies FTS_NOSTAT by overriding
+ * the follow check in fts_stat(). In theory, FTS could easily be
+ * changed to only stat links (to check what they point to) in the
+ * FTS_LOGICAL | FTS_NOSTAT case, which would produce a different
+ * result here, so keep the test around in case that ever happens.
+ */
+ atf_tc_expect_fail("FTS_LOGICAL nullifies FTS_NOSTAT");
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_LOGICAL | FTS_NOSTAT,
+ (struct fts_expect[]){
+ { FTS_DL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_NSOK, "file", "dir/file" },
+ { FTS_D, "up", "dir/up" },
+ { FTS_DL, "dead", "dir/up/dead" },
+ { FTS_DC, "dir", "dir/up/dir" },
+ { FTS_DC, "dirl", "dir/up/dirl" },
+ { FTS_NSOK, "file", "dir/up/file" },
+ { FTS_NSOK, "filel", "dir/up/filel" },
+ { FTS_DP, "up", "dir/up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_D, "dirl", "dirl" },
+ { FTS_NSOK, "file", "dirl/file" },
+ { FTS_D, "up", "dirl/up" },
+ { FTS_DL, "dead", "dirl/up/dead" },
+ { FTS_DC, "dir", "dirl/up/dir" },
+ { FTS_DC, "dirl", "dirl/up/dirl" },
+ { FTS_NSOK, "file", "dirl/up/file" },
+ { FTS_NSOK, "filel", "dirl/up/filel" },
+ { FTS_DP, "up", "dirl/up" },
+ { FTS_DP, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_F, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TC(fts_options_logical_seedot);
+ATF_TC_HEAD(fts_options_logical_seedot, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_LOGICAL | FTS_SEEDOT");
+}
+ATF_TC_BODY(fts_options_logical_seedot, tc)
+{
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_LOGICAL | FTS_SEEDOT,
+ (struct fts_expect[]){
+ { FTS_DL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_DOT, ".", "dir/." },
+ { FTS_DOT, "..", "dir/.." },
+ { FTS_F, "file", "dir/file" },
+ { FTS_D, "up", "dir/up" },
+ { FTS_DOT, ".", "dir/up/." },
+ { FTS_DOT, "..", "dir/up/.." },
+ { FTS_DL, "dead", "dir/up/dead" },
+ { FTS_DC, "dir", "dir/up/dir" },
+ { FTS_DC, "dirl", "dir/up/dirl" },
+ { FTS_F, "file", "dir/up/file" },
+ { FTS_F, "filel", "dir/up/filel" },
+ { FTS_DP, "up", "dir/up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_D, "dirl", "dirl" },
+ { FTS_DOT, ".", "dirl/." },
+ { FTS_DOT, "..", "dirl/.." },
+ { FTS_F, "file", "dirl/file" },
+ { FTS_D, "up", "dirl/up" },
+ { FTS_DOT, ".", "dirl/up/." },
+ { FTS_DOT, "..", "dirl/up/.." },
+ { FTS_DL, "dead", "dirl/up/dead" },
+ { FTS_DC, "dir", "dirl/up/dir" },
+ { FTS_DC, "dirl", "dirl/up/dirl" },
+ { FTS_F, "file", "dirl/up/file" },
+ { FTS_F, "filel", "dirl/up/filel" },
+ { FTS_DP, "up", "dirl/up" },
+ { FTS_DP, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_F, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TC(fts_options_physical);
+ATF_TC_HEAD(fts_options_physical, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL");
+}
+ATF_TC_BODY(fts_options_physical, tc)
+{
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_PHYSICAL,
+ (struct fts_expect[]){
+ { FTS_SL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "up", "up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_SL, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TC(fts_options_physical_nochdir);
+ATF_TC_HEAD(fts_options_physical_nochdir, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL | FTS_NOCHDIR");
+}
+ATF_TC_BODY(fts_options_physical_nochdir, tc)
+{
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_PHYSICAL | FTS_NOCHDIR,
+ (struct fts_expect[]){
+ { FTS_SL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_F, "file", "dir/file" },
+ { FTS_SL, "up", "dir/up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_SL, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TC(fts_options_physical_comfollow);
+ATF_TC_HEAD(fts_options_physical_comfollow, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL | FTS_COMFOLLOW");
+}
+ATF_TC_BODY(fts_options_physical_comfollow, tc)
+{
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_PHYSICAL | FTS_COMFOLLOW,
+ (struct fts_expect[]){
+ { FTS_DL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "up", "up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_D, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "up", "up" },
+ { FTS_DP, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_F, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TC(fts_options_physical_comfollowdir);
+ATF_TC_HEAD(fts_options_physical_comfollowdir, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL | FTS_COMFOLLOWDIR");
+}
+ATF_TC_BODY(fts_options_physical_comfollowdir, tc)
+{
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_PHYSICAL | FTS_COMFOLLOWDIR,
+ (struct fts_expect[]){
+ { FTS_DL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "up", "up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_D, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "up", "up" },
+ { FTS_DP, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TC(fts_options_physical_nostat);
+ATF_TC_HEAD(fts_options_physical_nostat, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL | FTS_NOSTAT");
+}
+ATF_TC_BODY(fts_options_physical_nostat, tc)
+{
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_PHYSICAL | FTS_NOSTAT,
+ (struct fts_expect[]){
+ { FTS_SL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_NSOK, "file", "file" },
+ { FTS_NSOK, "up", "up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_SL, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TC(fts_options_physical_nostat_type);
+ATF_TC_HEAD(fts_options_physical_nostat_type, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL | FTS_NOSTAT_TYPE");
+}
+ATF_TC_BODY(fts_options_physical_nostat_type, tc)
+{
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_PHYSICAL | FTS_NOSTAT_TYPE,
+ (struct fts_expect[]){
+ { FTS_SL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "up", "up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_SL, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+ATF_TC(fts_options_physical_seedot);
+ATF_TC_HEAD(fts_options_physical_seedot, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "FTS_PHYSICAL | FTS_SEEDOT");
+}
+ATF_TC_BODY(fts_options_physical_seedot, tc)
+{
+ fts_options_prepare(tc);
+ fts_test(tc, &(struct fts_testcase){
+ all_paths,
+ FTS_PHYSICAL | FTS_SEEDOT,
+ (struct fts_expect[]){
+ { FTS_SL, "dead", "dead" },
+ { FTS_D, "dir", "dir" },
+ { FTS_DOT, ".", "." },
+ { FTS_DOT, "..", ".." },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "up", "up" },
+ { FTS_DP, "dir", "dir" },
+ { FTS_SL, "dirl", "dirl" },
+ { FTS_F, "file", "file" },
+ { FTS_SL, "filel", "filel" },
+ { FTS_NS, "noent", "noent" },
+ { 0 }
+ },
+ });
+}
+
+/*
+ * TODO: Add tests for FTS_XDEV and FTS_WHITEOUT
+ */
+
+ATF_TP_ADD_TCS(tp)
+{
+ fts_check_debug();
+ ATF_TP_ADD_TC(tp, fts_options_logical);
+ ATF_TP_ADD_TC(tp, fts_options_logical_nostat);
+ ATF_TP_ADD_TC(tp, fts_options_logical_seedot);
+ ATF_TP_ADD_TC(tp, fts_options_physical);
+ ATF_TP_ADD_TC(tp, fts_options_physical_nochdir);
+ ATF_TP_ADD_TC(tp, fts_options_physical_comfollow);
+ ATF_TP_ADD_TC(tp, fts_options_physical_comfollowdir);
+ ATF_TP_ADD_TC(tp, fts_options_physical_nostat);
+ ATF_TP_ADD_TC(tp, fts_options_physical_nostat_type);
+ ATF_TP_ADD_TC(tp, fts_options_physical_seedot);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/fts_test.h b/lib/libc/tests/gen/fts_test.h
new file mode 100644
index 000000000000..b3f15050f265
--- /dev/null
+++ b/lib/libc/tests/gen/fts_test.h
@@ -0,0 +1,81 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef FTS_TEST_H_INCLUDED
+#define FTS_TEST_H_INCLUDED
+
+struct fts_expect {
+ int fts_info;
+ const char *fts_name;
+ const char *fts_accpath;
+};
+
+struct fts_testcase {
+ char **paths;
+ int fts_options;
+ struct fts_expect *fts_expect;
+};
+
+/* shorter name for dead links */
+#define FTS_DL FTS_SLNONE
+
+/* are we being debugged? */
+static bool fts_test_debug;
+
+/*
+ * Set debug flag if appropriate.
+ */
+static void
+fts_check_debug(void)
+{
+ fts_test_debug = !getenv("__RUNNING_INSIDE_ATF_RUN") &&
+ isatty(STDERR_FILENO);
+}
+
+/*
+ * Lexical order for reproducability.
+ */
+static int
+fts_lexical_compar(const FTSENT * const *a, const FTSENT * const *b)
+{
+ return (strcmp((*a)->fts_name, (*b)->fts_name));
+}
+
+/*
+ * Run FTS with the specified paths and options and verify that it
+ * produces the expected result in the correct order.
+ */
+static void
+fts_test(const struct atf_tc *tc, const struct fts_testcase *fts_tc)
+{
+ FTS *fts;
+ FTSENT *ftse;
+ const struct fts_expect *expect = fts_tc->fts_expect;
+ long level = 0;
+
+ fts = fts_open(fts_tc->paths, fts_tc->fts_options, fts_lexical_compar);
+ ATF_REQUIRE_MSG(fts != NULL, "fts_open(): %m");
+ while ((ftse = fts_read(fts)) != NULL && expect->fts_name != NULL) {
+ if (expect->fts_info == FTS_DP || expect->fts_info == FTS_DNR)
+ level--;
+ if (fts_test_debug) {
+ fprintf(stderr, "%2ld %2d %s\n", level,
+ ftse->fts_info, ftse->fts_name);
+ }
+ ATF_CHECK_STREQ(expect->fts_name, ftse->fts_name);
+ ATF_CHECK_STREQ(expect->fts_accpath, ftse->fts_accpath);
+ ATF_CHECK_INTEQ(expect->fts_info, ftse->fts_info);
+ ATF_CHECK_INTEQ(level, ftse->fts_level);
+ if (expect->fts_info == FTS_D)
+ level++;
+ expect++;
+ }
+ ATF_CHECK_EQ(NULL, ftse);
+ ATF_CHECK_EQ(NULL, expect->fts_name);
+ ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
+}
+
+#endif /* FTS_TEST_H_INCLUDED */
diff --git a/lib/libc/tests/gen/ftw_test.c b/lib/libc/tests/gen/ftw_test.c
new file mode 100644
index 000000000000..3d2cf3446dee
--- /dev/null
+++ b/lib/libc/tests/gen/ftw_test.c
@@ -0,0 +1,124 @@
+/*-
+ * Copyright (c) 2012 Jilles Tjoelker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Limited test program for nftw() as specified by IEEE Std. 1003.1-2008.
+ */
+
+#include <sys/wait.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ftw.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <spawn.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+extern char **environ;
+
+static char template[] = "testftw.XXXXXXXXXX";
+static char dir[PATH_MAX];
+static int ftwflags;
+
+static int
+cb(const char *path, const struct stat *st, int type, struct FTW *f)
+{
+
+ switch (type) {
+ case FTW_D:
+ if ((ftwflags & FTW_DEPTH) == 0)
+ return (0);
+ break;
+ case FTW_DP:
+ if ((ftwflags & FTW_DEPTH) != 0)
+ return (0);
+ break;
+ case FTW_SL:
+ if ((ftwflags & FTW_PHYS) != 0)
+ return (0);
+ break;
+ }
+ ATF_CHECK_MSG(false,
+ "unexpected path=%s type=%d f.level=%d\n",
+ path, type, f->level);
+ return (0);
+}
+
+ATF_TC_WITHOUT_HEAD(ftw_test);
+ATF_TC_BODY(ftw_test, tc)
+{
+ int fd;
+
+ ATF_REQUIRE_MSG(mkdtemp(template) != NULL, "mkdtemp failed");
+
+ /* XXX: the path needs to be absolute for the 0/FTW_DEPTH testcases */
+ ATF_REQUIRE_MSG(realpath(template, dir) != NULL,
+ "realpath failed; errno=%d", errno);
+
+ fd = open(dir, O_DIRECTORY|O_RDONLY);
+ ATF_REQUIRE_MSG(fd != -1, "open failed; errno=%d", errno);
+
+ ATF_REQUIRE_MSG(mkdirat(fd, "d1", 0777) == 0,
+ "mkdirat failed; errno=%d", errno);
+
+ ATF_REQUIRE_MSG(symlinkat(dir, fd, "d1/looper") == 0,
+ "symlinkat failed; errno=%d", errno);
+
+ printf("ftwflags=FTW_PHYS\n");
+ ftwflags = FTW_PHYS;
+ ATF_REQUIRE_MSG(nftw(dir, cb, 10, ftwflags) != -1,
+ "nftw FTW_PHYS failed; errno=%d", errno);
+
+ printf("ftwflags=FTW_PHYS|FTW_DEPTH\n");
+ ftwflags = FTW_PHYS|FTW_DEPTH;
+ ATF_REQUIRE_MSG(nftw(dir, cb, 10, ftwflags) != -1,
+ "nftw FTW_PHYS|FTW_DEPTH failed; errno=%d", errno);
+
+ printf("ftwflags=0\n");
+ ftwflags = 0;
+ ATF_REQUIRE_MSG(nftw(dir, cb, 10, ftwflags) != -1,
+ "nftw 0 failed; errno=%d", errno);
+
+ printf("ftwflags=FTW_DEPTH\n");
+ ftwflags = FTW_DEPTH;
+ ATF_REQUIRE_MSG(nftw(dir, cb, 10, ftwflags) != -1,
+ "nftw FTW_DEPTH failed; errno=%d", errno);
+
+ close(fd);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, ftw_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/getentropy_test.c b/lib/libc/tests/gen/getentropy_test.c
new file mode 100644
index 000000000000..6ac9d5678ea6
--- /dev/null
+++ b/lib/libc/tests/gen/getentropy_test.c
@@ -0,0 +1,85 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018 Conrad Meyer <cem@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <errno.h>
+#include <limits.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(getentropy_count);
+ATF_TC_BODY(getentropy_count, tc)
+{
+ char buf[2];
+ int ret;
+
+ /* getentropy(2) does not modify buf past the requested length */
+ buf[1] = 0x7C;
+ ret = getentropy(buf, 1);
+ ATF_REQUIRE_EQ(ret, 0);
+ ATF_REQUIRE_EQ(buf[1], 0x7C);
+}
+
+ATF_TC_WITHOUT_HEAD(getentropy_fault);
+ATF_TC_BODY(getentropy_fault, tc)
+{
+ int ret;
+
+ ret = getentropy(NULL, 1);
+ ATF_REQUIRE_EQ(ret, -1);
+ ATF_REQUIRE_EQ(errno, EFAULT);
+}
+
+ATF_TC_WITHOUT_HEAD(getentropy_sizes);
+ATF_TC_BODY(getentropy_sizes, tc)
+{
+ char buf[512];
+
+ ATF_REQUIRE_EQ(getentropy(buf, sizeof(buf)), -1);
+ ATF_REQUIRE_EQ(errno, EINVAL);
+ ATF_REQUIRE_EQ(getentropy(buf, GETENTROPY_MAX + 1), -1);
+ ATF_REQUIRE_EQ(errno, EINVAL);
+
+ /* Smaller sizes always succeed: */
+ ATF_REQUIRE_EQ(getentropy(buf, GETENTROPY_MAX), 0);
+ ATF_REQUIRE_EQ(getentropy(buf, GETENTROPY_MAX / 2), 0);
+ ATF_REQUIRE_EQ(getentropy(buf, 0), 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ signal(SIGSYS, SIG_IGN);
+
+ ATF_TP_ADD_TC(tp, getentropy_count);
+ ATF_TP_ADD_TC(tp, getentropy_fault);
+ ATF_TP_ADD_TC(tp, getentropy_sizes);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/getmntinfo_test.c b/lib/libc/tests/gen/getmntinfo_test.c
new file mode 100644
index 000000000000..06e1091d8a15
--- /dev/null
+++ b/lib/libc/tests/gen/getmntinfo_test.c
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2017 Conrad Meyer <cem@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Limited test program for getmntinfo(3), a non-standard BSDism.
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/ucred.h>
+
+#include <errno.h>
+
+#include <atf-c.h>
+
+static void
+check_mntinfo(struct statfs *mntinfo, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++) {
+ ATF_REQUIRE_MSG(mntinfo[i].f_version == STATFS_VERSION, "%ju",
+ (uintmax_t)mntinfo[i].f_version);
+ ATF_REQUIRE(mntinfo[i].f_namemax <= sizeof(mntinfo[0].f_mntonname));
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(getmntinfo_test);
+ATF_TC_BODY(getmntinfo_test, tc)
+{
+ int nmnts;
+ struct statfs *mntinfo;
+
+ /* Test bogus mode */
+ nmnts = getmntinfo(&mntinfo, 199);
+ ATF_REQUIRE_MSG(nmnts == 0 && errno == EINVAL,
+ "getmntinfo() succeeded; errno=%d", errno);
+
+ /* Valid modes */
+ nmnts = getmntinfo(&mntinfo, MNT_NOWAIT);
+ ATF_REQUIRE_MSG(nmnts != 0, "getmntinfo(MNT_NOWAIT) failed; errno=%d",
+ errno);
+
+ check_mntinfo(mntinfo, nmnts);
+ memset(mntinfo, 0xdf, sizeof(*mntinfo) * nmnts);
+
+ nmnts = getmntinfo(&mntinfo, MNT_WAIT);
+ ATF_REQUIRE_MSG(nmnts != 0, "getmntinfo(MNT_WAIT) failed; errno=%d",
+ errno);
+
+ check_mntinfo(mntinfo, nmnts);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, getmntinfo_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/glob2_test.c b/lib/libc/tests/gen/glob2_test.c
new file mode 100644
index 000000000000..ff1b36b830b8
--- /dev/null
+++ b/lib/libc/tests/gen/glob2_test.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2017 Dell EMC Isilon
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+/*
+ * Derived from Russ Cox' pathological case test program used for the
+ * https://research.swtch.com/glob article.
+ */
+ATF_TC_WITHOUT_HEAD(glob_pathological_test);
+ATF_TC_BODY(glob_pathological_test, tc)
+{
+ struct timespec t, t2;
+ glob_t g;
+ const char *longname = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
+ char pattern[1000], *p;
+ double dt;
+ unsigned i, j, k, mul;
+ int fd, rc;
+
+ fd = open(longname, O_CREAT | O_RDWR, 0666);
+ ATF_REQUIRE(fd >= 0);
+
+ /*
+ * Test up to 100 a* groups. Exponential implementations typically go
+ * bang at i=7 or 8.
+ */
+ for (i = 0; i < 100; i++) {
+ /*
+ * Create a*...b pattern with i 'a*' groups.
+ */
+ p = pattern;
+ for (k = 0; k < i; k++) {
+ *p++ = 'a';
+ *p++ = '*';
+ }
+ *p++ = 'b';
+ *p = '\0';
+
+ clock_gettime(CLOCK_REALTIME, &t);
+ for (j = 0; j < mul; j++) {
+ memset(&g, 0, sizeof g);
+ rc = glob(pattern, 0, 0, &g);
+ if (rc == GLOB_NOSPACE || rc == GLOB_ABORTED) {
+ ATF_REQUIRE_MSG(rc == GLOB_NOMATCH,
+ "an unexpected error occurred: "
+ "rc=%d errno=%d", rc, errno);
+ /* NORETURN */
+ }
+
+ ATF_CHECK_MSG(rc == GLOB_NOMATCH,
+ "A bogus match occurred: '%s' ~ '%s'", pattern,
+ g.gl_pathv[0]);
+ globfree(&g);
+ }
+ clock_gettime(CLOCK_REALTIME, &t2);
+
+ t2.tv_sec -= t.tv_sec;
+ t2.tv_nsec -= t.tv_nsec;
+ dt = t2.tv_sec + (double)t2.tv_nsec/1e9;
+ dt /= mul;
+
+ ATF_CHECK_MSG(dt < 1, "glob(3) took far too long: %d %.9f", i,
+ dt);
+
+ if (dt >= 0.0001)
+ mul = 1;
+ }
+}
+
+ATF_TC(glob_period);
+ATF_TC_HEAD(glob_period, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test behaviour when matching files that start with a period"
+ "(documented in the glob(3) CAVEATS section).");
+}
+ATF_TC_BODY(glob_period, tc)
+{
+ int i;
+ glob_t g;
+
+ atf_utils_create_file(".test", "");
+ glob(".", 0, NULL, &g);
+ ATF_REQUIRE_MSG(g.gl_matchc == 1,
+ "glob(3) shouldn't match files starting with a period when using '.'");
+ for (i = 0; i < g.gl_matchc; i++)
+ printf("%s\n", g.gl_pathv[i]);
+ glob(".*", 0, NULL, &g);
+ ATF_REQUIRE_MSG(g.gl_matchc == 3 && strcmp(g.gl_pathv[2], ".test") == 0,
+ "glob(3) should match files starting with a period when using '.*'");
+}
+
+static bool glob_callback_invoked;
+
+static int
+errfunc(const char *path, int err)
+{
+ ATF_CHECK_STREQ(path, "test/");
+ ATF_CHECK(err == EACCES);
+ glob_callback_invoked = true;
+ /* Suppress EACCES errors. */
+ return (0);
+}
+
+ATF_TC(glob_callback);
+ATF_TC_HEAD(glob_callback, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test ability of callback function to suppress errors");
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(glob_callback, tc)
+{
+ glob_t g;
+ int rv;
+
+ ATF_REQUIRE_EQ(0, mkdir("test", 0755));
+ ATF_REQUIRE_EQ(0, symlink("foo", "test/foo"));
+ ATF_REQUIRE_EQ(0, chmod("test", 0));
+
+ glob_callback_invoked = false;
+ rv = glob("test/*", 0, errfunc, &g);
+ ATF_CHECK_MSG(glob_callback_invoked,
+ "glob(3) failed to invoke callback function");
+ ATF_CHECK_EQ_MSG(GLOB_NOMATCH, rv,
+ "callback function failed to suppress EACCES");
+ globfree(&g);
+
+ /* GLOB_ERR should ignore the suppressed error. */
+ glob_callback_invoked = false;
+ rv = glob("test/*", GLOB_ERR, errfunc, &g);
+ ATF_CHECK_MSG(glob_callback_invoked,
+ "glob(3) failed to invoke callback function");
+ ATF_CHECK_EQ_MSG(GLOB_ABORTED, rv,
+ "GLOB_ERR didn't override callback function");
+ globfree(&g);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, glob_pathological_test);
+ ATF_TP_ADD_TC(tp, glob_period);
+ ATF_TP_ADD_TC(tp, glob_callback);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/glob_blocks_test.c b/lib/libc/tests/gen/glob_blocks_test.c
new file mode 100644
index 000000000000..629b90bee762
--- /dev/null
+++ b/lib/libc/tests/gen/glob_blocks_test.c
@@ -0,0 +1,62 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <glob.h>
+#include <stdbool.h>
+
+#include <atf-c.h>
+
+ATF_TC(glob_b_callback);
+ATF_TC_HEAD(glob_b_callback, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test ability of callback block to suppress errors");
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(glob_b_callback, tc)
+{
+ static bool glob_callback_invoked;
+ static int (^errblk)(const char *, int) =
+ ^(const char *path, int err) {
+ ATF_CHECK_STREQ(path, "test/");
+ ATF_CHECK(err == EACCES);
+ glob_callback_invoked = true;
+ /* Suppress EACCES errors. */
+ return (0);
+ };
+ glob_t g;
+ int rv;
+
+ ATF_REQUIRE_EQ(0, mkdir("test", 0755));
+ ATF_REQUIRE_EQ(0, symlink("foo", "test/foo"));
+ ATF_REQUIRE_EQ(0, chmod("test", 0));
+
+ glob_callback_invoked = false;
+ rv = glob_b("test/*", 0, errblk, &g);
+ ATF_CHECK_MSG(glob_callback_invoked,
+ "glob(3) failed to invoke callback block");
+ ATF_CHECK_EQ_MSG(GLOB_NOMATCH, rv,
+ "callback function failed to suppress EACCES");
+ globfree(&g);
+
+ /* GLOB_ERR should ignore the suppressed error. */
+ glob_callback_invoked = false;
+ rv = glob_b("test/*", GLOB_ERR, errblk, &g);
+ ATF_CHECK_MSG(glob_callback_invoked,
+ "glob(3) failed to invoke callback block");
+ ATF_CHECK_EQ_MSG(GLOB_ABORTED, rv,
+ "GLOB_ERR didn't override callback block");
+ globfree(&g);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, glob_b_callback);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/makecontext_test.c b/lib/libc/tests/gen/makecontext_test.c
new file mode 100644
index 000000000000..23e3cf85f677
--- /dev/null
+++ b/lib/libc/tests/gen/makecontext_test.c
@@ -0,0 +1,185 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018 John H. Baldwin <jhb@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this 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 <atf-c.h>
+#include <ucontext.h>
+
+static char uc_stack[16 * 1024];
+
+static void
+check_1(int arg1)
+{
+
+ ATF_REQUIRE_EQ(arg1, 1);
+}
+
+ATF_TC_WITHOUT_HEAD(makecontext_arg1);
+ATF_TC_BODY(makecontext_arg1, tc)
+{
+ ucontext_t ctx[2];
+
+ ATF_REQUIRE_EQ(getcontext(&ctx[1]), 0);
+ ctx[1].uc_stack.ss_sp = uc_stack;
+ ctx[1].uc_stack.ss_size = sizeof(uc_stack);
+ ctx[1].uc_link = &ctx[0];
+ makecontext(&ctx[1], (void (*)(void))check_1, 1, 1);
+
+ ATF_REQUIRE_EQ(swapcontext(&ctx[0], &ctx[1]), 0);
+}
+
+static void
+check_2(int arg1, int arg2)
+{
+
+ ATF_REQUIRE_EQ(arg1, 1);
+ ATF_REQUIRE_EQ(arg2, 2);
+}
+
+ATF_TC_WITHOUT_HEAD(makecontext_arg2);
+ATF_TC_BODY(makecontext_arg2, tc)
+{
+ ucontext_t ctx[2];
+
+ ATF_REQUIRE_EQ(getcontext(&ctx[1]), 0);
+ ctx[1].uc_stack.ss_sp = uc_stack;
+ ctx[1].uc_stack.ss_size = sizeof(uc_stack);
+ ctx[1].uc_link = &ctx[0];
+ makecontext(&ctx[1], (void (*)(void))check_2, 2, 1, 2);
+
+ ATF_REQUIRE_EQ(swapcontext(&ctx[0], &ctx[1]), 0);
+}
+
+static void
+check_3(int arg1, int arg2, int arg3)
+{
+
+ ATF_REQUIRE_EQ(arg1, 1);
+ ATF_REQUIRE_EQ(arg2, 2);
+ ATF_REQUIRE_EQ(arg3, 3);
+}
+
+ATF_TC_WITHOUT_HEAD(makecontext_arg3);
+ATF_TC_BODY(makecontext_arg3, tc)
+{
+ ucontext_t ctx[2];
+
+ ATF_REQUIRE_EQ(getcontext(&ctx[1]), 0);
+ ctx[1].uc_stack.ss_sp = uc_stack;
+ ctx[1].uc_stack.ss_size = sizeof(uc_stack);
+ ctx[1].uc_link = &ctx[0];
+ makecontext(&ctx[1], (void (*)(void))check_3, 3, 1, 2, 3);
+
+ ATF_REQUIRE_EQ(swapcontext(&ctx[0], &ctx[1]), 0);
+}
+
+static void
+check_4(int arg1, int arg2, int arg3, int arg4)
+{
+
+ ATF_REQUIRE_EQ(arg1, 1);
+ ATF_REQUIRE_EQ(arg2, 2);
+ ATF_REQUIRE_EQ(arg3, 3);
+ ATF_REQUIRE_EQ(arg4, 4);
+}
+
+ATF_TC_WITHOUT_HEAD(makecontext_arg4);
+ATF_TC_BODY(makecontext_arg4, tc)
+{
+ ucontext_t ctx[2];
+
+ ATF_REQUIRE_EQ(getcontext(&ctx[1]), 0);
+ ctx[1].uc_stack.ss_sp = uc_stack;
+ ctx[1].uc_stack.ss_size = sizeof(uc_stack);
+ ctx[1].uc_link = &ctx[0];
+ makecontext(&ctx[1], (void (*)(void))check_4, 4, 1, 2, 3, 4);
+
+ ATF_REQUIRE_EQ(swapcontext(&ctx[0], &ctx[1]), 0);
+}
+
+static void
+check_5(int arg1, int arg2, int arg3, int arg4, int arg5)
+{
+
+ ATF_REQUIRE_EQ(arg1, 1);
+ ATF_REQUIRE_EQ(arg2, 2);
+ ATF_REQUIRE_EQ(arg3, 3);
+ ATF_REQUIRE_EQ(arg4, 4);
+ ATF_REQUIRE_EQ(arg5, 5);
+}
+
+ATF_TC_WITHOUT_HEAD(makecontext_arg5);
+ATF_TC_BODY(makecontext_arg5, tc)
+{
+ ucontext_t ctx[2];
+
+ ATF_REQUIRE_EQ(getcontext(&ctx[1]), 0);
+ ctx[1].uc_stack.ss_sp = uc_stack;
+ ctx[1].uc_stack.ss_size = sizeof(uc_stack);
+ ctx[1].uc_link = &ctx[0];
+ makecontext(&ctx[1], (void (*)(void))check_5, 5, 1, 2, 3, 4, 5);
+
+ ATF_REQUIRE_EQ(swapcontext(&ctx[0], &ctx[1]), 0);
+}
+
+static void
+check_6(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6)
+{
+
+ ATF_REQUIRE_EQ(arg1, 1);
+ ATF_REQUIRE_EQ(arg2, 2);
+ ATF_REQUIRE_EQ(arg3, 3);
+ ATF_REQUIRE_EQ(arg4, 4);
+ ATF_REQUIRE_EQ(arg5, 5);
+ ATF_REQUIRE_EQ(arg6, 6);
+}
+
+ATF_TC_WITHOUT_HEAD(makecontext_arg6);
+ATF_TC_BODY(makecontext_arg6, tc)
+{
+ ucontext_t ctx[2];
+
+ ATF_REQUIRE_EQ(getcontext(&ctx[1]), 0);
+ ctx[1].uc_stack.ss_sp = uc_stack;
+ ctx[1].uc_stack.ss_size = sizeof(uc_stack);
+ ctx[1].uc_link = &ctx[0];
+ makecontext(&ctx[1], (void (*)(void))check_6, 6, 1, 2, 3, 4, 5, 6);
+
+ ATF_REQUIRE_EQ(swapcontext(&ctx[0], &ctx[1]), 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, makecontext_arg1);
+ ATF_TP_ADD_TC(tp, makecontext_arg2);
+ ATF_TP_ADD_TC(tp, makecontext_arg3);
+ ATF_TP_ADD_TC(tp, makecontext_arg4);
+ ATF_TP_ADD_TC(tp, makecontext_arg5);
+ ATF_TP_ADD_TC(tp, makecontext_arg6);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/opendir_test.c b/lib/libc/tests/gen/opendir_test.c
new file mode 100644
index 000000000000..b7481255654f
--- /dev/null
+++ b/lib/libc/tests/gen/opendir_test.c
@@ -0,0 +1,145 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+/*
+ * Create a directory with a single subdirectory.
+ */
+static void
+opendir_prepare(const struct atf_tc *tc)
+{
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE_EQ(0, mkdir("dir/subdir", 0755));
+}
+
+/*
+ * Assuming dirp represents the directory created by opendir_prepare(),
+ * verify that readdir() returns what we expected to see there.
+ */
+static void
+opendir_check(const struct atf_tc *tc, DIR *dirp)
+{
+ struct dirent *ent;
+
+ ATF_REQUIRE((ent = readdir(dirp)) != NULL);
+ ATF_CHECK_EQ(1, ent->d_namlen);
+ ATF_CHECK_STREQ(".", ent->d_name);
+ ATF_CHECK_EQ(DT_DIR, ent->d_type);
+ ATF_REQUIRE((ent = readdir(dirp)) != NULL);
+ ATF_CHECK_EQ(2, ent->d_namlen);
+ ATF_CHECK_STREQ("..", ent->d_name);
+ ATF_CHECK_EQ(DT_DIR, ent->d_type);
+ ATF_REQUIRE((ent = readdir(dirp)) != NULL);
+ ATF_CHECK_EQ(sizeof("subdir") - 1, ent->d_namlen);
+ ATF_CHECK_STREQ("subdir", ent->d_name);
+ ATF_CHECK_EQ(DT_DIR, ent->d_type);
+ ATF_CHECK(readdir(dirp) == NULL);
+ ATF_CHECK(readdir(dirp) == NULL);
+}
+
+ATF_TC(opendir_ok);
+ATF_TC_HEAD(opendir_ok, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Open a directory.");
+}
+ATF_TC_BODY(opendir_ok, tc)
+{
+ DIR *dirp;
+
+ opendir_prepare(tc);
+ ATF_REQUIRE((dirp = opendir("dir")) != NULL);
+ opendir_check(tc, dirp);
+ ATF_CHECK_EQ(0, closedir(dirp));
+}
+
+ATF_TC(opendir_fifo);
+ATF_TC_HEAD(opendir_fifo, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Do not hang if given a named pipe.");
+}
+ATF_TC_BODY(opendir_fifo, tc)
+{
+ DIR *dirp;
+ int fd;
+
+ ATF_REQUIRE((fd = mkfifo("fifo", 0644)) >= 0);
+ ATF_REQUIRE_EQ(0, close(fd));
+ ATF_REQUIRE((dirp = opendir("fifo")) == NULL);
+ ATF_CHECK_EQ(ENOTDIR, errno);
+}
+
+ATF_TC(fdopendir_ok);
+ATF_TC_HEAD(fdopendir_ok, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Open a directory from a directory descriptor.");
+}
+ATF_TC_BODY(fdopendir_ok, tc)
+{
+ DIR *dirp;
+ int dd;
+
+ opendir_prepare(tc);
+ ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ ATF_REQUIRE((dirp = fdopendir(dd)) != NULL);
+ opendir_check(tc, dirp);
+ ATF_CHECK_EQ(dd, fdclosedir(dirp));
+ ATF_CHECK_EQ(0, close(dd));
+}
+
+ATF_TC(fdopendir_ebadf);
+ATF_TC_HEAD(fdopendir_ebadf, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Open a directory from an invalid descriptor.");
+}
+ATF_TC_BODY(fdopendir_ebadf, tc)
+{
+ DIR *dirp;
+ int dd;
+
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ ATF_CHECK_EQ(0, close(dd));
+ ATF_REQUIRE((dirp = fdopendir(dd)) == NULL);
+ ATF_CHECK_EQ(EBADF, errno);
+}
+
+ATF_TC(fdopendir_enotdir);
+ATF_TC_HEAD(fdopendir_enotdir, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Open a directory from a non-directory descriptor.");
+}
+ATF_TC_BODY(fdopendir_enotdir, tc)
+{
+ DIR *dirp;
+ int fd;
+
+ ATF_REQUIRE((fd = open("file", O_CREAT | O_RDWR, 0644)) >= 0);
+ ATF_REQUIRE((dirp = fdopendir(fd)) == NULL);
+ ATF_CHECK_EQ(ENOTDIR, errno);
+ ATF_CHECK_EQ(0, close(fd));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, opendir_ok);
+ ATF_TP_ADD_TC(tp, fdopendir_ok);
+ ATF_TP_ADD_TC(tp, fdopendir_ebadf);
+ ATF_TP_ADD_TC(tp, fdopendir_enotdir);
+ ATF_TP_ADD_TC(tp, opendir_fifo);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/popen_test.c b/lib/libc/tests/gen/popen_test.c
new file mode 100644
index 000000000000..43eadd380f39
--- /dev/null
+++ b/lib/libc/tests/gen/popen_test.c
@@ -0,0 +1,248 @@
+/*-
+ * Copyright (c) 2013 Jilles Tjoelker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Limited test program for popen() as specified by IEEE Std. 1003.1-2008,
+ * with BSD extensions.
+ */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+static volatile sig_atomic_t got_sigpipe;
+
+static void
+sigpipe_handler(int sig __unused)
+{
+ got_sigpipe = 1;
+}
+
+static void
+check_cloexec(FILE *fp, const char *mode)
+{
+ int exp_flags, flags;
+
+ flags = fcntl(fileno(fp), F_GETFD);
+ ATF_CHECK_MSG(flags != -1, "fcntl(F_GETFD) failed; errno=%d", errno);
+ if (flags == -1)
+ return;
+ if (strchr(mode, 'e') != NULL)
+ exp_flags = FD_CLOEXEC;
+ else
+ exp_flags = 0;
+ ATF_CHECK_MSG((flags & FD_CLOEXEC) == exp_flags,
+ "bad cloexec flag; %d != %d", flags, exp_flags);
+}
+
+ATF_TC_WITHOUT_HEAD(popen_all_modes_test);
+ATF_TC_BODY(popen_all_modes_test, tc)
+{
+ FILE *fp;
+ int i, status;
+ const char *mode;
+ const char *allmodes[] = { "r", "w", "r+", "re", "we", "r+e", "re+" };
+
+ for (i = 0; i < nitems(allmodes); i++) {
+ mode = allmodes[i];
+ fp = popen("exit 7", mode);
+ ATF_CHECK_MSG(fp != NULL, "popen(, \"%s\") failed", mode);
+ if (fp == NULL)
+ continue;
+ check_cloexec(fp, mode);
+ status = pclose(fp);
+ ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 7,
+ "bad exit status (no I/O)");
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(popen_rmodes_test);
+ATF_TC_BODY(popen_rmodes_test, tc)
+{
+ FILE *fp;
+ const char *rmodes[] = { "r", "r+", "re", "r+e", "re+" };
+ const char *mode;
+ char buf[80];
+ int i, status;
+
+ for (i = 0; i < nitems(rmodes); i++) {
+ mode = rmodes[i];
+ fp = popen("exit 9", mode);
+ ATF_CHECK_MSG(fp != NULL, "popen(, \"%s\") failed", mode);
+ if (fp == NULL)
+ continue;
+ check_cloexec(fp, mode);
+ bool input_error_1 = !(fgetc(fp) != EOF || !feof(fp) || !ferror(fp));
+ ATF_CHECK_MSG(!input_error_1, "input error 1");
+ if (input_error_1)
+ continue;
+ status = pclose(fp);
+ ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 9,
+ "bad exit status (input)");
+ }
+
+ for (i = 0; i < nitems(rmodes); i++) {
+ char *sres;
+ mode = rmodes[i];
+ fp = popen("echo hi there", mode);
+ ATF_CHECK_MSG(fp != NULL, "popen(, \"%s\") failed", mode);
+ if (fp == NULL)
+ continue;
+ check_cloexec(fp, mode);
+ ATF_CHECK_MSG((sres = fgets(buf, sizeof(buf), fp)) != NULL,
+ "Input error 2");
+ if (sres != NULL)
+ ATF_CHECK_MSG(strcmp(buf, "hi there\n") == 0,
+ "Bad input 1");
+ status = pclose(fp);
+ ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0,
+ "Bad exit status (input)");
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(popen_wmodes_test);
+ATF_TC_BODY(popen_wmodes_test, tc)
+{
+ FILE *fp, *fp2;
+ const char *wmodes[] = { "w", "r+", "we", "r+e", "re+" };
+ const char *mode;
+ struct sigaction act, oact;
+ int i, j, status;
+
+ for (i = 0; i < nitems(wmodes); i++) {
+ mode = wmodes[i];
+ fp = popen("read x && [ \"$x\" = abcd ]", mode);
+ ATF_CHECK_MSG(fp != NULL, "popen(, \"%s\") failed", mode);
+ if (fp == NULL)
+ continue;
+ check_cloexec(fp, mode);
+ ATF_CHECK_MSG(fputs("abcd\n", fp) != EOF,
+ "Output error 1");
+ status = pclose(fp);
+ ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0,
+ "Bad exit status (output)");
+ }
+
+ act.sa_handler = sigpipe_handler;
+ act.sa_flags = SA_RESTART;
+ sigemptyset(&act.sa_mask);
+ ATF_CHECK_MSG(sigaction(SIGPIPE, &act, &oact) != -1,
+ "sigaction() failed");
+ for (i = 0; i < nitems(wmodes); i++) {
+ mode = wmodes[i];
+ fp = popen("exit 88", mode);
+ ATF_CHECK_MSG(fp != NULL, "popen(, \"%s\") failed", mode);
+ if (fp == NULL)
+ continue;
+ check_cloexec(fp, mode);
+ got_sigpipe = 0;
+ while (fputs("abcd\n", fp) != EOF)
+ ;
+ ATF_CHECK_MSG(ferror(fp) && errno == EPIPE, "Expected EPIPE");
+ ATF_CHECK_MSG(got_sigpipe, "Expected SIGPIPE");
+ status = pclose(fp);
+ ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 88,
+ "Bad exit status (EPIPE)");
+ }
+ ATF_CHECK_MSG(sigaction(SIGPIPE, &oact, NULL) != -1,
+ "sigaction() failed");
+
+ for (i = 0; i < nitems(wmodes); i++) {
+ for (j = 0; j < nitems(wmodes); j++) {
+ mode = wmodes[i];
+ fp = popen("read x", mode);
+ ATF_CHECK_MSG(fp != NULL,
+ "popen(, \"%s\") failed", mode);
+ if (fp == NULL)
+ continue;
+ mode = wmodes[j];
+ fp2 = popen("read x", mode);
+ ATF_CHECK_MSG(fp2 != NULL,
+ "popen(, \"%s\") failed", mode);
+ if (fp2 == NULL) {
+ pclose(fp);
+ continue;
+ }
+ /* If fp2 inherits fp's pipe, we will deadlock here. */
+ status = pclose(fp);
+ ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 1,
+ "bad exit status (2 pipes)");
+ status = pclose(fp2);
+ ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 1,
+ "bad exit status (2 pipes)");
+ }
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(popen_rwmodes_test);
+ATF_TC_BODY(popen_rwmodes_test, tc)
+{
+ const char *rwmodes[] = { "r+", "r+e", "re+" };
+ FILE *fp;
+ const char *mode;
+ char *sres;
+ char buf[80];
+ int i, ires, status;
+
+ for (i = 0; i < nitems(rwmodes); i++) {
+ mode = rwmodes[i];
+ fp = popen("read x && printf '%s\\n' \"Q${x#a}\"", mode);
+ ATF_CHECK_MSG(fp != NULL, "popen(, \"%s\") failed", mode);
+ if (fp == NULL)
+ continue;
+ check_cloexec(fp, mode);
+ ATF_CHECK_MSG((ires = fputs("abcd\n", fp)) != EOF,
+ "Output error 2");
+ if (ires != EOF) {
+ sres = fgets(buf, sizeof(buf), fp);
+ ATF_CHECK_MSG(sres != NULL, "Input error 3");
+ if (sres != NULL)
+ ATF_CHECK_MSG(strcmp(buf, "Qbcd\n") == 0,
+ "Bad input 2");
+ }
+ status = pclose(fp);
+ ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0,
+ "bad exit status (I/O)");
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, popen_all_modes_test);
+ ATF_TP_ADD_TC(tp, popen_rmodes_test);
+ ATF_TP_ADD_TC(tp, popen_wmodes_test);
+ ATF_TP_ADD_TC(tp, popen_rwmodes_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/posix_spawn/Makefile b/lib/libc/tests/gen/posix_spawn/Makefile
new file mode 100644
index 000000000000..df428876708b
--- /dev/null
+++ b/lib/libc/tests/gen/posix_spawn/Makefile
@@ -0,0 +1,32 @@
+.include <bsd.own.mk>
+
+BINDIR= ${TESTSDIR}
+
+NETBSD_ATF_TESTS_C= fileactions_test
+NETBSD_ATF_TESTS_C+= spawn_test
+NETBSD_ATF_TESTS_C+= spawnattr_test
+
+PROGS= h_fileactions
+PROGS+= h_spawn
+PROGS+= h_spawnattr
+
+SCRIPTS= h_nonexec
+SCRIPTS+= h_zero
+
+CLEANFILES+= h_nonexec
+
+.include "../../Makefile.netbsd-tests"
+
+# The dd status=none option is non-standard. Only use it when this test succeeds
+# rather than require dd to be a bootstrap tool.
+DD_NOSTATUS!=(dd status=none count=0 2> /dev/null && echo status=none) || true
+DD=dd ${DD_NOSTATUS}
+h_zero:
+ ${DD} if=/dev/zero of=h_zero bs=1k count=2
+ chmod a+x h_zero
+
+CLEANFILES+= h_zero
+
+WARNS?=3
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/gen/posix_spawn/Makefile.depend b/lib/libc/tests/gen/posix_spawn/Makefile.depend
new file mode 100644
index 000000000000..e89a5c52c82a
--- /dev/null
+++ b/lib/libc/tests/gen/posix_spawn/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libnetbsd \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/gen/posix_spawn_test.c b/lib/libc/tests/gen/posix_spawn_test.c
new file mode 100644
index 000000000000..22133cf1d59a
--- /dev/null
+++ b/lib/libc/tests/gen/posix_spawn_test.c
@@ -0,0 +1,186 @@
+/*-
+ * Copyright (c) 2011 Jilles Tjoelker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test program for posix_spawn() and posix_spawnp() as specified by
+ * IEEE Std. 1003.1-2008.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <spawn.h>
+
+#include <atf-c.h>
+
+static const char true_script[] =
+ "#!/usr/bin/env\n"
+ "/usr/bin/true\n";
+
+char *myenv[2] = { "answer=42", NULL };
+
+ATF_TC_WITHOUT_HEAD(posix_spawn_simple_test);
+ATF_TC_BODY(posix_spawn_simple_test, tc)
+{
+ char *myargs[4];
+ int error, status;
+ pid_t pid, waitres;
+
+ /* Make sure we have no child processes. */
+ while (waitpid(-1, NULL, 0) != -1)
+ ;
+ ATF_REQUIRE_MSG(errno == ECHILD, "errno was not ECHILD: %d", errno);
+
+ /* Simple test. */
+ myargs[0] = "sh";
+ myargs[1] = "-c";
+ myargs[2] = "exit $answer";
+ myargs[3] = NULL;
+ error = posix_spawnp(&pid, myargs[0], NULL, NULL, myargs, myenv);
+ ATF_REQUIRE(error == 0);
+ waitres = waitpid(pid, &status, 0);
+ ATF_REQUIRE(waitres == pid);
+ ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 42);
+}
+
+ATF_TC_WITHOUT_HEAD(posix_spawn_no_such_command_negative_test);
+ATF_TC_BODY(posix_spawn_no_such_command_negative_test, tc)
+{
+ char *myargs[4];
+ int error, status;
+ pid_t pid, waitres;
+
+ /*
+ * If the executable does not exist, the function shall either fail
+ * and not create a child process or succeed and create a child
+ * process that exits with status 127.
+ */
+ myargs[0] = "/var/empty/nonexistent";
+ myargs[1] = NULL;
+ error = posix_spawn(&pid, myargs[0], NULL, NULL, myargs, myenv);
+ if (error == 0) {
+ waitres = waitpid(pid, &status, 0);
+ ATF_REQUIRE(waitres == pid);
+ ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 127);
+ } else {
+ ATF_REQUIRE(error == ENOENT);
+ waitres = waitpid(-1, NULL, 0);
+ ATF_REQUIRE(waitres == -1 && errno == ECHILD);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(posix_spawnp_enoexec_fallback);
+ATF_TC_BODY(posix_spawnp_enoexec_fallback, tc)
+{
+ char buf[FILENAME_MAX];
+ char *myargs[2];
+ int error, status;
+ pid_t pid, waitres;
+
+ snprintf(buf, sizeof(buf), "%s/spawnp_enoexec.sh",
+ atf_tc_get_config_var(tc, "srcdir"));
+ myargs[0] = buf;
+ myargs[1] = NULL;
+ error = posix_spawnp(&pid, myargs[0], NULL, NULL, myargs, myenv);
+ ATF_REQUIRE(error == 0);
+ waitres = waitpid(pid, &status, 0);
+ ATF_REQUIRE(waitres == pid);
+ ATF_REQUIRE(WIFEXITED(status) && WEXITSTATUS(status) == 42);
+}
+
+ATF_TC_WITHOUT_HEAD(posix_spawnp_enoexec_fallback_null_argv0);
+ATF_TC_BODY(posix_spawnp_enoexec_fallback_null_argv0, tc)
+{
+ char buf[FILENAME_MAX];
+ char *myargs[1];
+ int error;
+ pid_t pid;
+
+ snprintf(buf, sizeof(buf), "%s/spawnp_enoexec.sh",
+ atf_tc_get_config_var(tc, "srcdir"));
+ myargs[0] = NULL;
+ error = posix_spawnp(&pid, buf, NULL, NULL, myargs, myenv);
+ ATF_REQUIRE(error == EINVAL);
+}
+
+ATF_TC(posix_spawnp_eacces);
+ATF_TC_HEAD(posix_spawnp_eacces, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Verify EACCES behavior in posix_spawnp");
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(posix_spawnp_eacces, tc)
+{
+ const struct spawnp_eacces_tc {
+ const char *pathvar;
+ int error_expected;
+ } spawnp_eacces_tests[] = {
+ { ".", EACCES }, /* File exists, but not +x */
+ { "unsearchable", ENOENT }, /* File exists, dir not +x */
+ };
+ char *myargs[2] = { "eacces", NULL };
+ int error;
+
+ error = mkdir("unsearchable", 0755);
+ ATF_REQUIRE(error == 0);
+ error = symlink("/usr/bin/true", "unsearchable/eacces");
+ ATF_REQUIRE(error == 0);
+
+ (void)chmod("unsearchable", 0444);
+
+ /* this will create a non-executable file */
+ atf_utils_create_file("eacces", true_script);
+
+ for (size_t i = 0; i < nitems(spawnp_eacces_tests); i++) {
+ const struct spawnp_eacces_tc *tc = &spawnp_eacces_tests[i];
+ pid_t pid;
+
+ error = setenv("PATH", tc->pathvar, 1);
+ ATF_REQUIRE_EQ(0, error);
+
+ error = posix_spawnp(&pid, myargs[0], NULL, NULL, myargs,
+ myenv);
+ ATF_CHECK_INTEQ_MSG(tc->error_expected, error,
+ "path '%s'", tc->pathvar);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, posix_spawn_simple_test);
+ ATF_TP_ADD_TC(tp, posix_spawn_no_such_command_negative_test);
+ ATF_TP_ADD_TC(tp, posix_spawnp_enoexec_fallback);
+ ATF_TP_ADD_TC(tp, posix_spawnp_enoexec_fallback_null_argv0);
+ ATF_TP_ADD_TC(tp, posix_spawnp_eacces);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/realpath2_test.c b/lib/libc/tests/gen/realpath2_test.c
new file mode 100644
index 000000000000..f89dd99cbb72
--- /dev/null
+++ b/lib/libc/tests/gen/realpath2_test.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2017 Jan Kokemüller
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+ATF_TC(realpath_buffer_overflow);
+ATF_TC_HEAD(realpath_buffer_overflow, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test for out of bounds read from 'left' array "
+ "(compile realpath.c with '-fsanitize=address')");
+}
+
+ATF_TC_BODY(realpath_buffer_overflow, tc)
+{
+ char path[MAXPATHLEN] = { 0 };
+ char resb[MAXPATHLEN] = { 0 };
+ size_t i;
+
+ path[0] = 'a';
+ path[1] = '/';
+ for (i = 2; i < sizeof(path) - 1; ++i) {
+ path[i] = 'a';
+ }
+
+ ATF_REQUIRE(realpath(path, resb) == NULL);
+}
+
+ATF_TC(realpath_empty_symlink);
+ATF_TC_HEAD(realpath_empty_symlink, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test for correct behavior when encountering empty symlinks");
+}
+
+ATF_TC_BODY(realpath_empty_symlink, tc)
+{
+ char path[MAXPATHLEN] = { 0 };
+ char slnk[MAXPATHLEN] = { 0 };
+ char resb[MAXPATHLEN] = { 0 };
+ int fd;
+
+ (void)strlcat(slnk, "empty_symlink", sizeof(slnk));
+
+ ATF_REQUIRE(symlink("", slnk) == 0);
+
+ fd = open("aaa", O_RDONLY | O_CREAT, 0600);
+
+ ATF_REQUIRE(fd >= 0);
+ ATF_REQUIRE(close(fd) == 0);
+
+ (void)strlcat(path, "empty_symlink", sizeof(path));
+ (void)strlcat(path, "/aaa", sizeof(path));
+
+ ATF_REQUIRE_ERRNO(ENOENT, realpath(path, resb) == NULL);
+
+ ATF_REQUIRE(unlink("aaa") == 0);
+ ATF_REQUIRE(unlink(slnk) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, realpath_buffer_overflow);
+ ATF_TP_ADD_TC(tp, realpath_empty_symlink);
+
+ return atf_no_error();
+}
diff --git a/lib/libc/tests/gen/scandir_blocks_test.c b/lib/libc/tests/gen/scandir_blocks_test.c
new file mode 100644
index 000000000000..b94270bc410e
--- /dev/null
+++ b/lib/libc/tests/gen/scandir_blocks_test.c
@@ -0,0 +1,118 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+static void
+scandir_blocks_prepare(const struct atf_tc *tc)
+{
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE_EQ(0, mkdir("dir/dir", 0755));
+ ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644)));
+ ATF_REQUIRE_EQ(0, symlink("file", "dir/link"));
+ ATF_REQUIRE_EQ(0, mkdir("dir/skip", 0755));
+}
+
+static void
+scandir_blocks_verify(const struct atf_tc *tc, int n, struct dirent **namelist)
+{
+ ATF_REQUIRE_EQ_MSG(5, n, "return value is %d", n);
+ ATF_CHECK_STREQ("link", namelist[0]->d_name);
+ ATF_CHECK_STREQ("file", namelist[1]->d_name);
+ ATF_CHECK_STREQ("dir", namelist[2]->d_name);
+ ATF_CHECK_STREQ("..", namelist[3]->d_name);
+ ATF_CHECK_STREQ(".", namelist[4]->d_name);
+}
+
+ATF_TC(scandir_b_test);
+ATF_TC_HEAD(scandir_b_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test scandir_b()");
+}
+ATF_TC_BODY(scandir_b_test, tc)
+{
+ struct dirent **namelist = NULL;
+ int i, ret;
+
+ scandir_blocks_prepare(tc);
+ ret = scandir_b("dir", &namelist,
+ ^(const struct dirent *ent) {
+ return (strcmp(ent->d_name, "skip") != 0);
+ },
+ ^(const struct dirent **a, const struct dirent **b) {
+ return (strcmp((*b)->d_name, (*a)->d_name));
+ });
+ scandir_blocks_verify(tc, ret, namelist);
+ for (i = 0; i < ret; i++)
+ free(namelist[i]);
+ free(namelist);
+}
+
+ATF_TC(fdscandir_b_test);
+ATF_TC_HEAD(fdscandir_b_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test fdscandir_b()");
+}
+ATF_TC_BODY(fdscandir_b_test, tc)
+{
+ struct dirent **namelist = NULL;
+ int fd, i, ret;
+
+ scandir_blocks_prepare(tc);
+ ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ ret = fdscandir_b(fd, &namelist,
+ ^(const struct dirent *ent) {
+ return (strcmp(ent->d_name, "skip") != 0);
+ },
+ ^(const struct dirent **a, const struct dirent **b) {
+ return (strcmp((*b)->d_name, (*a)->d_name));
+ });
+ scandir_blocks_verify(tc, ret, namelist);
+ for (i = 0; i < ret; i++)
+ free(namelist[i]);
+ free(namelist);
+ ATF_REQUIRE_EQ(0, close(fd));
+}
+
+ATF_TC(scandirat_b_test);
+ATF_TC_HEAD(scandirat_b_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test scandirat_b()");
+}
+ATF_TC_BODY(scandirat_b_test, tc)
+{
+ struct dirent **namelist = NULL;
+ int fd, i, ret;
+
+ scandir_blocks_prepare(tc);
+ ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_SEARCH)) >= 0);
+ ret = scandirat_b(fd, ".", &namelist,
+ ^(const struct dirent *ent) {
+ return (strcmp(ent->d_name, "skip") != 0);
+ },
+ ^(const struct dirent **a, const struct dirent **b) {
+ return (strcmp((*b)->d_name, (*a)->d_name));
+ });
+ scandir_blocks_verify(tc, ret, namelist);
+ for (i = 0; i < ret; i++)
+ free(namelist[i]);
+ free(namelist);
+ ATF_REQUIRE_EQ(0, close(fd));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, scandir_b_test);
+ ATF_TP_ADD_TC(tp, fdscandir_b_test);
+ ATF_TP_ADD_TC(tp, scandirat_b_test);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/scandir_test.c b/lib/libc/tests/gen/scandir_test.c
new file mode 100644
index 000000000000..afd25bf7c0b2
--- /dev/null
+++ b/lib/libc/tests/gen/scandir_test.c
@@ -0,0 +1,195 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+static void
+scandir_prepare(const struct atf_tc *tc)
+{
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ ATF_REQUIRE_EQ(0, mkdir("dir/dir", 0755));
+ ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644)));
+ ATF_REQUIRE_EQ(0, symlink("file", "dir/link"));
+ ATF_REQUIRE_EQ(0, mkdir("dir/skip", 0755));
+}
+
+static void
+scandir_verify(const struct atf_tc *tc, int n, struct dirent **namelist)
+{
+ ATF_REQUIRE_EQ_MSG(5, n, "return value is %d", n);
+ ATF_CHECK_STREQ("link", namelist[0]->d_name);
+ ATF_CHECK_STREQ("file", namelist[1]->d_name);
+ ATF_CHECK_STREQ("dir", namelist[2]->d_name);
+ ATF_CHECK_STREQ("..", namelist[3]->d_name);
+ ATF_CHECK_STREQ(".", namelist[4]->d_name);
+}
+
+static int
+scandir_select(const struct dirent *ent)
+{
+ return (strcmp(ent->d_name, "skip") != 0);
+}
+
+static int
+scandir_compare(const struct dirent **a, const struct dirent **b)
+{
+ return (strcmp((*b)->d_name, (*a)->d_name));
+}
+
+ATF_TC(scandir_test);
+ATF_TC_HEAD(scandir_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test scandir()");
+}
+ATF_TC_BODY(scandir_test, tc)
+{
+ struct dirent **namelist = NULL;
+ int i, ret;
+
+ scandir_prepare(tc);
+ ret = scandir("dir", &namelist, scandir_select, scandir_compare);
+ scandir_verify(tc, ret, namelist);
+ for (i = 0; i < ret; i++)
+ free(namelist[i]);
+ free(namelist);
+}
+
+ATF_TC(fdscandir_test);
+ATF_TC_HEAD(fdscandir_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test fdscandir()");
+}
+ATF_TC_BODY(fdscandir_test, tc)
+{
+ struct dirent **namelist = NULL;
+ int fd, i, ret;
+
+ scandir_prepare(tc);
+ ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ ret = fdscandir(fd, &namelist, scandir_select, scandir_compare);
+ scandir_verify(tc, ret, namelist);
+ for (i = 0; i < ret; i++)
+ free(namelist[i]);
+ free(namelist);
+ ATF_REQUIRE_EQ(0, close(fd));
+}
+
+ATF_TC(scandirat_test);
+ATF_TC_HEAD(scandirat_test, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test scandirat()");
+}
+ATF_TC_BODY(scandirat_test, tc)
+{
+ struct dirent **namelist = NULL;
+ int fd, i, ret;
+
+ scandir_prepare(tc);
+ ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_SEARCH)) >= 0);
+ ret = scandirat(fd, ".", &namelist, scandir_select, scandir_compare);
+ scandir_verify(tc, ret, namelist);
+ for (i = 0; i < ret; i++)
+ free(namelist[i]);
+ free(namelist);
+ ATF_REQUIRE_EQ(0, close(fd));
+}
+
+static int
+scandir_none(const struct dirent *ent __unused)
+{
+ return (0);
+}
+
+ATF_TC(scandir_none);
+ATF_TC_HEAD(scandir_none, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test scandir() when no entries are selected");
+}
+ATF_TC_BODY(scandir_none, tc)
+{
+ struct dirent **namelist = NULL;
+
+ ATF_REQUIRE_EQ(0, scandir(".", &namelist, scandir_none, alphasort));
+ ATF_REQUIRE(namelist);
+ free(namelist);
+}
+
+/*
+ * Test that scandir() propagates errors from readdir(): we create a
+ * directory with enough entries that it can't be read in a single
+ * getdirentries() call, then abuse the selection callback to close the
+ * file descriptor scandir() is using after the first call, causing the
+ * next one to fail, and verify that readdir() returns an error instead of
+ * a partial result. We make two passes, one in which nothing was
+ * selected before the error occurred, and one in which everything was.
+ */
+static int scandir_error_count;
+static int scandir_error_fd;
+static int scandir_error_select_return;
+
+static int
+scandir_error_select(const struct dirent *ent __unused)
+{
+ if (scandir_error_count++ == 0)
+ close(scandir_error_fd);
+ return (scandir_error_select_return);
+}
+
+ATF_TC(scandir_error);
+ATF_TC_HEAD(scandir_error, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test that scandir() propagates errors from readdir()");
+}
+ATF_TC_BODY(scandir_error, tc)
+{
+ char path[16];
+ struct dirent **namelist = NULL;
+ int fd, i;
+
+ ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
+ for (i = 0; i < 1024; i++) {
+ snprintf(path, sizeof(path), "dir/%04x", i);
+ ATF_REQUIRE_EQ(0, symlink(path + 4, path));
+ }
+
+ /* first pass, select nothing */
+ ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ scandir_error_count = 0;
+ scandir_error_fd = fd;
+ scandir_error_select_return = 0;
+ ATF_CHECK_ERRNO(EBADF,
+ fdscandir(fd, &namelist, scandir_error_select, NULL) < 0);
+ ATF_CHECK_EQ(NULL, namelist);
+
+ /* second pass, select everything */
+ ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
+ scandir_error_count = 0;
+ scandir_error_fd = fd;
+ scandir_error_select_return = 1;
+ ATF_CHECK_ERRNO(EBADF,
+ fdscandir(fd, &namelist, scandir_error_select, NULL) < 0);
+ ATF_CHECK_EQ(NULL, namelist);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, scandir_test);
+ ATF_TP_ADD_TC(tp, fdscandir_test);
+ ATF_TP_ADD_TC(tp, scandirat_test);
+ ATF_TP_ADD_TC(tp, scandir_none);
+ ATF_TP_ADD_TC(tp, scandir_error);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/sig2str_test.c b/lib/libc/tests/gen/sig2str_test.c
new file mode 100644
index 000000000000..00b6ebb2349a
--- /dev/null
+++ b/lib/libc/tests/gen/sig2str_test.c
@@ -0,0 +1,213 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Ricardo Branco <rbranco@suse.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+static void
+test_roundtrip(int signum)
+{
+ char str[SIG2STR_MAX];
+ int sig;
+
+ ATF_REQUIRE_EQ_MSG(sig2str(signum, str), 0,
+ "sig2str(%d) failed", signum);
+ ATF_REQUIRE_EQ_MSG(str2sig(str, &sig), 0,
+ "str2sig(\"%s\") failed", str);
+ ATF_REQUIRE_INTEQ_MSG(sig, signum,
+ "Mismatch: roundtrip conversion gave %d instead of %d",
+ sig, signum);
+}
+
+ATF_TC_WITHOUT_HEAD(sig2str_valid);
+ATF_TC_BODY(sig2str_valid, tc)
+{
+ int sig;
+
+ for (sig = 1; sig < sys_nsig; sig++) {
+ test_roundtrip(sig);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(sig2str_invalid);
+ATF_TC_BODY(sig2str_invalid, tc)
+{
+ char buf[SIG2STR_MAX];
+
+ ATF_CHECK(sig2str(0, buf) != 0);
+ ATF_CHECK(sig2str(-1, buf) != 0);
+ ATF_CHECK(sig2str(SIGRTMAX + 1, buf) != 0);
+}
+
+ATF_TC_WITHOUT_HEAD(str2sig_rtmin_rtmax);
+ATF_TC_BODY(str2sig_rtmin_rtmax, tc)
+{
+ int sig;
+
+ ATF_CHECK_MSG(str2sig("RTMIN", &sig) == 0,
+ "str2sig(\"RTMIN\") failed");
+ ATF_CHECK_INTEQ_MSG(sig, SIGRTMIN,
+ "RTMIN mapped to %d, expected %d", sig, SIGRTMIN);
+
+ ATF_CHECK_MSG(str2sig("RTMAX", &sig) == 0,
+ "str2sig(\"RTMAX\") failed");
+ ATF_CHECK_INTEQ_MSG(sig, SIGRTMAX,
+ "RTMAX mapped to %d, expected %d", sig, SIGRTMAX);
+
+ ATF_CHECK_MSG(str2sig("RTMIN+1", &sig) == 0,
+ "str2sig(\"RTMIN+1\") failed");
+ ATF_CHECK_INTEQ_MSG(sig, SIGRTMIN + 1,
+ "RTMIN+1 mapped to %d, expected %d", sig, SIGRTMIN + 1);
+
+ ATF_CHECK_MSG(str2sig("RTMAX-1", &sig) == 0,
+ "str2sig(\"RTMAX-1\") failed");
+ ATF_CHECK_INTEQ_MSG(sig, SIGRTMAX - 1,
+ "RTMAX-1 mapped to %d, expected %d", sig, SIGRTMAX - 1);
+}
+
+ATF_TC_WITHOUT_HEAD(str2sig_invalid_rt);
+ATF_TC_BODY(str2sig_invalid_rt, tc)
+{
+ int i, sig;
+
+ const char *invalid[] = {
+ "RTMIN+0",
+ "RTMAX-0",
+ "RTMIN-777",
+ "RTMIN+777",
+ "RTMAX-777",
+ "RTMAX+777",
+ "RTMIN-",
+ "RTMAX-",
+ "RTMIN0",
+ "RTMAX1",
+ "RTMIN+abc",
+ "RTMIN-abc",
+ NULL
+ };
+
+ for (i = 0; invalid[i] != NULL; i++) {
+ ATF_CHECK_MSG(str2sig(invalid[i], &sig) != 0,
+ "str2sig(\"%s\") unexpectedly succeeded", invalid[i]);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(str2sig_fullname);
+ATF_TC_BODY(str2sig_fullname, tc)
+{
+ char fullname[SIG2STR_MAX + 3];
+ int n, sig;
+
+ for (sig = 1; sig < sys_nsig; sig++) {
+ snprintf(fullname, sizeof(fullname), "SIG%s", sys_signame[sig]);
+
+ ATF_CHECK_MSG(str2sig(fullname, &n) == 0,
+ "str2sig(\"%s\") failed with errno %d (%s)",
+ fullname, errno, strerror(errno));
+
+ ATF_CHECK_INTEQ_MSG(n, sig,
+ "Mismatch: %s = %d, str2sig(\"%s\") = %d",
+ sys_signame[sig], sig, fullname, n);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(str2sig_lowercase);
+ATF_TC_BODY(str2sig_lowercase, tc)
+{
+ char fullname[SIG2STR_MAX + 3];
+ int n, sig;
+
+ for (sig = 1; sig < sys_nsig; sig++) {
+ snprintf(fullname, sizeof(fullname), "sig%s", sys_signame[sig]);
+ for (size_t i = 3; i < strlen(fullname); i++)
+ fullname[i] = toupper((unsigned char)fullname[i]);
+
+ ATF_CHECK_MSG(str2sig(fullname, &n) == 0,
+ "str2sig(\"%s\") failed with errno %d (%s)",
+ fullname, errno, strerror(errno));
+
+ ATF_CHECK_INTEQ_MSG(n, sig,
+ "Mismatch: %s = %d, str2sig(\"%s\") = %d",
+ sys_signame[sig], sig, fullname, n);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(str2sig_numeric);
+ATF_TC_BODY(str2sig_numeric, tc)
+{
+ char buf[16];
+ int n, sig;
+
+ for (sig = NSIG; sig < SIGRTMIN; sig++) {
+ snprintf(buf, sizeof(buf), "%d", sig);
+ ATF_CHECK_MSG(str2sig(buf, &n) == 0,
+ "str2sig(\"%s\") failed", buf);
+ ATF_CHECK_INTEQ_MSG(n, sig,
+ "Mismatch: str2sig(\"%s\") = %d, expected %d",
+ buf, n, sig);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(str2sig_invalid);
+ATF_TC_BODY(str2sig_invalid, tc)
+{
+ const char *invalid[] = {
+ "SIGDOESNOTEXIST",
+ "DOESNOTEXIST",
+ "INTERRUPT",
+ "",
+ "SIG",
+ "123abc",
+ "sig1extra",
+ NULL
+ };
+ int i, sig;
+
+ for (i = 0; invalid[i] != NULL; i++) {
+ ATF_CHECK_MSG(str2sig(invalid[i], &sig) != 0,
+ "str2sig(\"%s\") unexpectedly succeeded", invalid[i]);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, sig2str_valid);
+ ATF_TP_ADD_TC(tp, sig2str_invalid);
+ ATF_TP_ADD_TC(tp, str2sig_rtmin_rtmax);
+ ATF_TP_ADD_TC(tp, str2sig_invalid_rt);
+ ATF_TP_ADD_TC(tp, str2sig_fullname);
+ ATF_TP_ADD_TC(tp, str2sig_lowercase);
+ ATF_TP_ADD_TC(tp, str2sig_numeric);
+ ATF_TP_ADD_TC(tp, str2sig_invalid);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/sigsetops_test.c b/lib/libc/tests/gen/sigsetops_test.c
new file mode 100644
index 000000000000..a22c4b3f4f59
--- /dev/null
+++ b/lib/libc/tests/gen/sigsetops_test.c
@@ -0,0 +1,188 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2019 Kyle Evans <kevans@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdbool.h>
+
+#include <atf-c.h>
+
+/* Return the status of the specified sig's bit. */
+static bool
+sigbitstatus(const sigset_t *set, int sig)
+{
+
+ return (set->__bits[_SIG_WORD(sig)] & _SIG_BIT(sig)) != 0;
+}
+
+/* Verify that sig is the lone bit set in the sigset. */
+static void
+siglonebit(const sigset_t *set, int sig)
+{
+ int i;
+
+ for (i = 0; i < _SIG_WORDS; ++i) {
+ if (i != _SIG_WORD(sig))
+ ATF_REQUIRE_MSG(set->__bits[i] == 0,
+ "word %d altered to %x", i, set->__bits[i]);
+ else
+ ATF_REQUIRE_MSG((set->__bits[i] & ~_SIG_BIT(sig)) == 0,
+ "word %d has other bits set: %x", i,
+ set->__bits[i] & ~_SIG_BIT(sig));
+ }
+}
+
+static void
+sigcompare(const sigset_t *left, const sigset_t *right)
+{
+ int i;
+
+ for (i = 0; i < _SIG_WORDS; ++i) {
+ ATF_REQUIRE_MSG(left->__bits[i] == right->__bits[i],
+ "sig comparison failed at %d; left=%x, right=%x",
+ i, left->__bits[i], right->__bits[i]);
+ }
+}
+
+/*
+ * Test implementation details of our sigsetops... make sure the correct bits
+ * are getting set, for the most part, and that duplicate operations don't
+ * error out.
+ */
+ATF_TC_WITHOUT_HEAD(posix_sigsetop_test);
+ATF_TC_BODY(posix_sigsetop_test, tc)
+{
+ sigset_t set;
+ int i;
+
+ ATF_REQUIRE(sigfillset(&set) == 0);
+ for (i = 0; i < _SIG_WORDS; ++i) {
+ ATF_REQUIRE_MSG(set.__bits[i] == ~0U, "sigfillset failed @ %d",
+ i);
+ }
+ ATF_REQUIRE(sigemptyset(&set) == 0);
+ for (i = 0; i < _SIG_WORDS; ++i) {
+ ATF_REQUIRE_MSG(set.__bits[i] == 0, "sigemptyset failed @ %d",
+ i);
+ }
+ /* Ensure that sigismember reflects the empty set status. */
+ for (i = 1; i < NSIG; ++i) {
+ ATF_REQUIRE(sigismember(&set, i) == 0);
+ }
+
+ ATF_REQUIRE(sigaddset(&set, -1) == -1 && errno == EINVAL);
+ ATF_REQUIRE(sigaddset(&set, _SIG_MAXSIG + 1) == -1 && errno == EINVAL);
+ ATF_REQUIRE(sigdelset(&set, -1) == -1 && errno == EINVAL);
+ ATF_REQUIRE(sigdelset(&set, _SIG_MAXSIG + 1) == -1 && errno == EINVAL);
+
+ ATF_REQUIRE(sigaddset(&set, SIGSEGV) == 0);
+ ATF_REQUIRE(sigismember(&set, SIGSEGV) != 0);
+ ATF_REQUIRE_MSG(sigbitstatus(&set, SIGSEGV), "sigaddset failure");
+ siglonebit(&set, SIGSEGV);
+
+ /*
+ * A second addition should succeed without altering the state. This
+ * should be trivially true.
+ */
+ ATF_REQUIRE(sigaddset(&set, SIGSEGV) == 0);
+ ATF_REQUIRE_MSG(sigbitstatus(&set, SIGSEGV),
+ "sigaddset twice changed bit");
+
+ ATF_REQUIRE(sigdelset(&set, SIGSEGV) == 0);
+ ATF_REQUIRE_MSG(!sigbitstatus(&set, SIGSEGV), "sigdelset failure");
+ ATF_REQUIRE(sigismember(&set, SIGSEGV) == 0);
+ ATF_REQUIRE(sigdelset(&set, SIGSEGV) == 0);
+ ATF_REQUIRE_MSG(!sigbitstatus(&set, SIGSEGV),
+ "sigdelset twice changed bit");
+ for (i = 0; i < _SIG_WORDS; ++i) {
+ ATF_REQUIRE_MSG(set.__bits[i] == 0, "set not empty @ %d",
+ i);
+ }
+ for (i = 1; i < NSIG; ++i) {
+ ATF_REQUIRE(sigismember(&set, i) == 0);
+ }
+}
+
+/*
+ * Test extended sigset ops for union/intersection and testing of empty set.
+ */
+ATF_TC_WITHOUT_HEAD(extended_sigsetop_test);
+ATF_TC_BODY(extended_sigsetop_test, tc)
+{
+ sigset_t chkset, set1, set2, set3;
+
+ sigemptyset(&chkset);
+ sigemptyset(&set1);
+ sigemptyset(&set2);
+ ATF_REQUIRE(sigisemptyset(&chkset) != 0);
+ sigaddset(&set1, SIGSEGV);
+ sigaddset(&set2, SIGKILL);
+ sigaddset(&chkset, SIGSEGV);
+ ATF_REQUIRE(sigisemptyset(&chkset) == 0);
+ sigaddset(&chkset, SIGKILL);
+ ATF_REQUIRE(sigorset(&set3, &set1, &set2) == 0);
+ ATF_REQUIRE(sigbitstatus(&set3, SIGSEGV));
+ ATF_REQUIRE(sigbitstatus(&set3, SIGKILL));
+
+ /*
+ * chkset was built with our POSIX-specified set operations that we've
+ * already tested, so it's a good comparison.
+ */
+ sigcompare(&chkset, &set3);
+ /*
+ * Clear chkset; make sure sigisemptyset() still looks ok. sigaddset
+ * and sigdelset have already been tested to make sure that they're not
+ * touching other bits.
+ */
+ sigdelset(&chkset, SIGSEGV);
+ sigdelset(&chkset, SIGKILL);
+ ATF_REQUIRE(sigisemptyset(&chkset) != 0);
+ ATF_REQUIRE(sigandset(&set3, &set1, &set2) == 0);
+ /* Make sure we clobbered these. */
+ ATF_REQUIRE(!sigbitstatus(&set3, SIGSEGV));
+ ATF_REQUIRE(!sigbitstatus(&set3, SIGKILL));
+ ATF_REQUIRE(sigisemptyset(&set3) != 0);
+ /* Rebuild for sigandset test */
+ sigemptyset(&set1);
+ sigemptyset(&set2);
+ sigaddset(&set1, SIGSEGV);
+ sigaddset(&set1, SIGKILL);
+ sigaddset(&set2, SIGSEGV);
+ ATF_REQUIRE(sigandset(&set3, &set1, &set2) == 0);
+ ATF_REQUIRE(sigbitstatus(&set3, SIGSEGV));
+ ATF_REQUIRE(!sigbitstatus(&set3, SIGKILL));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, posix_sigsetop_test);
+ ATF_TP_ADD_TC(tp, extended_sigsetop_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/gen/spawnp_enoexec.sh b/lib/libc/tests/gen/spawnp_enoexec.sh
new file mode 100755
index 000000000000..1050b7a6f944
--- /dev/null
+++ b/lib/libc/tests/gen/spawnp_enoexec.sh
@@ -0,0 +1,3 @@
+# Intentionally no interpreter
+
+exit 42
diff --git a/lib/libc/tests/gen/test-fnmatch.c b/lib/libc/tests/gen/test-fnmatch.c
new file mode 100644
index 000000000000..1a6c6ed7efdf
--- /dev/null
+++ b/lib/libc/tests/gen/test-fnmatch.c
@@ -0,0 +1,99 @@
+/*-
+ * Copyright (c) 2010 Jilles Tjoelker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "fnmatch_testcases.h"
+
+static int
+write_sh_tests(const char *progname, int num)
+{
+ size_t i;
+ struct testcase *t;
+
+ printf("# Generated by %s -s %d, do not edit.\n", progname, num);
+ printf("# $" "FreeBSD$\n");
+ printf("failures=\n");
+ printf("failed() { printf '%%s\\n' \"Failed: $1 '$2' '$3'\"; failures=x$failures; }\n");
+ if (num == 1) {
+ printf("testmatch() { eval \"case \\$2 in ''$1) ;; *) failed testmatch \\\"\\$@\\\";; esac\"; }\n");
+ printf("testnomatch() { eval \"case \\$2 in ''$1) failed testnomatch \\\"\\$@\\\";; esac\"; }\n");
+ } else if (num == 2) {
+ printf("# We do not treat a backslash specially in this case,\n");
+ printf("# but this is not the case in all shells.\n");
+ printf("netestmatch() { case $2 in $1) ;; *) failed netestmatch \"$@\";; esac; }\n");
+ printf("netestnomatch() { case $2 in $1) failed netestnomatch \"$@\";; esac; }\n");
+ }
+
+ for (i = 0; i < nitems(testcases); i++) {
+ t = &testcases[i];
+ if (strchr(t->pattern, '\'') != NULL ||
+ strchr(t->string, '\'') != NULL)
+ continue;
+ if (t->flags == 0 && strcmp(t->pattern, "\\") == 0)
+ continue;
+ if (num == 1 && t->flags == 0)
+ printf("test%smatch '%s' '%s'\n",
+ t->result == FNM_NOMATCH ? "no" : "",
+ t->pattern, t->string);
+ if (num == 2 && (t->flags == FNM_NOESCAPE ||
+ (t->flags == 0 && strchr(t->pattern, '\\') == NULL)))
+ printf("netest%smatch '%s' '%s'\n",
+ t->result == FNM_NOMATCH ? "no" : "",
+ t->pattern, t->string);
+ }
+ printf("[ -z \"$failures\" ]\n");
+ return 0;
+}
+
+static void
+usage(char *progname)
+{
+ fprintf(stderr, "usage: %s [-s num]\n", progname);
+ fprintf(stderr, "-s option writes tests for sh(1), num is 1 or 2\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ int opt;
+
+ while ((opt = getopt(argc, argv, "s:")) != -1) {
+ switch (opt) {
+ case 's':
+ return (write_sh_tests(argv[0], atoi(optarg)));
+ default:
+ usage(argv[0]);
+ exit(1);
+ }
+ }
+ usage(argv[0]);
+ exit(1);
+}
diff --git a/lib/libc/tests/gen/wordexp_test.c b/lib/libc/tests/gen/wordexp_test.c
new file mode 100644
index 000000000000..a8b9d5509633
--- /dev/null
+++ b/lib/libc/tests/gen/wordexp_test.c
@@ -0,0 +1,383 @@
+/*-
+ * Copyright (c) 2003 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test program for wordexp() and wordfree() as specified by
+ * IEEE Std. 1003.1-2001.
+ */
+
+#include <sys/wait.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wordexp.h>
+
+#include <atf-c.h>
+
+static void
+chld_handler(int x)
+{
+ int status, serrno;
+
+ (void)x;
+ serrno = errno;
+ while (waitpid(-1, &status, WNOHANG) > 0)
+ ;
+ errno = serrno;
+}
+
+ATF_TC_WITHOUT_HEAD(simple_test);
+ATF_TC_BODY(simple_test, tc)
+{
+ wordexp_t we;
+ int r;
+
+ /* Test that the macros are there. */
+ (void)(WRDE_APPEND + WRDE_DOOFFS + WRDE_NOCMD + WRDE_REUSE +
+ WRDE_SHOWERR + WRDE_UNDEF);
+ (void)(WRDE_BADCHAR + WRDE_BADVAL + WRDE_CMDSUB + WRDE_NOSPACE +
+ WRDE_SYNTAX);
+
+ /* Simple test. */
+ r = wordexp("hello world", &we, 0);
+ ATF_REQUIRE(r == 0);
+ ATF_REQUIRE(we.we_wordc == 2);
+ ATF_REQUIRE(strcmp(we.we_wordv[0], "hello") == 0);
+ ATF_REQUIRE(strcmp(we.we_wordv[1], "world") == 0);
+ ATF_REQUIRE(we.we_wordv[2] == NULL);
+ wordfree(&we);
+}
+
+ATF_TC_WITHOUT_HEAD(long_output_test);
+ATF_TC_BODY(long_output_test, tc)
+{
+ char longdata[6 * 10000 + 1];
+ wordexp_t we;
+ int i, r;
+
+ /* Long output. */
+ for (i = 0; i < 10000; i++)
+ snprintf(longdata + 6 * i, 7, "%05d ", i);
+ r = wordexp(longdata, &we, 0);
+ ATF_REQUIRE(r == 0);
+ ATF_REQUIRE(we.we_wordc == 10000);
+ ATF_REQUIRE(we.we_wordv[10000] == NULL);
+ wordfree(&we);
+}
+
+ATF_TC_WITHOUT_HEAD(WRDE_DOOFFS_test);
+ATF_TC_BODY(WRDE_DOOFFS_test, tc)
+{
+ wordexp_t we;
+ int r;
+
+ we.we_offs = 3;
+ r = wordexp("hello world", &we, WRDE_DOOFFS);
+ ATF_REQUIRE(r == 0);
+ ATF_REQUIRE(we.we_wordc == 2);
+ ATF_REQUIRE(we.we_wordv[0] == NULL);
+ ATF_REQUIRE(we.we_wordv[1] == NULL);
+ ATF_REQUIRE(we.we_wordv[2] == NULL);
+ ATF_REQUIRE(strcmp(we.we_wordv[3], "hello") == 0);
+ ATF_REQUIRE(strcmp(we.we_wordv[4], "world") == 0);
+ ATF_REQUIRE(we.we_wordv[5] == NULL);
+ wordfree(&we);
+}
+
+ATF_TC_WITHOUT_HEAD(WRDE_REUSE_test);
+ATF_TC_BODY(WRDE_REUSE_test, tc)
+{
+ wordexp_t we;
+ int r;
+
+ r = wordexp("hello world", &we, 0);
+ r = wordexp("hello world", &we, WRDE_REUSE);
+ ATF_REQUIRE(r == 0);
+ ATF_REQUIRE(we.we_wordc == 2);
+ ATF_REQUIRE(strcmp(we.we_wordv[0], "hello") == 0);
+ ATF_REQUIRE(strcmp(we.we_wordv[1], "world") == 0);
+ ATF_REQUIRE(we.we_wordv[2] == NULL);
+ wordfree(&we);
+}
+
+ATF_TC_WITHOUT_HEAD(WRDE_APPEND_test);
+ATF_TC_BODY(WRDE_APPEND_test, tc)
+{
+ wordexp_t we;
+ int r;
+
+ r = wordexp("this is", &we, 0);
+ ATF_REQUIRE(r == 0);
+ r = wordexp("a test", &we, WRDE_APPEND);
+ ATF_REQUIRE(r == 0);
+ ATF_REQUIRE(we.we_wordc == 4);
+ ATF_REQUIRE(strcmp(we.we_wordv[0], "this") == 0);
+ ATF_REQUIRE(strcmp(we.we_wordv[1], "is") == 0);
+ ATF_REQUIRE(strcmp(we.we_wordv[2], "a") == 0);
+ ATF_REQUIRE(strcmp(we.we_wordv[3], "test") == 0);
+ ATF_REQUIRE(we.we_wordv[4] == NULL);
+ wordfree(&we);
+}
+
+ATF_TC_WITHOUT_HEAD(WRDE_DOOFFS__WRDE_APPEND_test);
+ATF_TC_BODY(WRDE_DOOFFS__WRDE_APPEND_test, tc)
+{
+ wordexp_t we;
+ int r;
+
+ we.we_offs = 2;
+ r = wordexp("this is", &we, WRDE_DOOFFS);
+ ATF_REQUIRE(r == 0);
+ r = wordexp("a test", &we, WRDE_APPEND|WRDE_DOOFFS);
+ ATF_REQUIRE(r == 0);
+ r = wordexp("of wordexp", &we, WRDE_APPEND|WRDE_DOOFFS);
+ ATF_REQUIRE(r == 0);
+ ATF_REQUIRE(we.we_wordc == 6);
+ ATF_REQUIRE(we.we_wordv[0] == NULL);
+ ATF_REQUIRE(we.we_wordv[1] == NULL);
+ ATF_REQUIRE(strcmp(we.we_wordv[2], "this") == 0);
+ ATF_REQUIRE(strcmp(we.we_wordv[3], "is") == 0);
+ ATF_REQUIRE(strcmp(we.we_wordv[4], "a") == 0);
+ ATF_REQUIRE(strcmp(we.we_wordv[5], "test") == 0);
+ ATF_REQUIRE(strcmp(we.we_wordv[6], "of") == 0);
+ ATF_REQUIRE(strcmp(we.we_wordv[7], "wordexp") == 0);
+ ATF_REQUIRE(we.we_wordv[8] == NULL);
+ wordfree(&we);
+}
+
+ATF_TC_WITHOUT_HEAD(WRDE_UNDEF_test);
+ATF_TC_BODY(WRDE_UNDEF_test, tc)
+{
+ wordexp_t we;
+ int r;
+
+ r = wordexp("${dont_set_me}", &we, WRDE_UNDEF);
+ ATF_REQUIRE(r == WRDE_BADVAL);
+}
+
+ATF_TC_WITHOUT_HEAD(WRDE_NOCMD_test);
+ATF_TC_BODY(WRDE_NOCMD_test, tc)
+{
+ wordexp_t we;
+ int r;
+
+ r = wordexp("`date`", &we, WRDE_NOCMD);
+ ATF_REQUIRE(r == WRDE_CMDSUB);
+ r = wordexp("\"`date`\"", &we, WRDE_NOCMD);
+ ATF_REQUIRE(r == WRDE_CMDSUB);
+ r = wordexp("$(date)", &we, WRDE_NOCMD);
+ ATF_REQUIRE(r == WRDE_CMDSUB);
+ r = wordexp("\"$(date)\"", &we, WRDE_NOCMD);
+ ATF_REQUIRE(r == WRDE_CMDSUB);
+ r = wordexp("$((3+5))", &we, WRDE_NOCMD);
+ ATF_REQUIRE(r == 0);
+ r = wordexp("\\$\\(date\\)", &we, WRDE_NOCMD|WRDE_REUSE);
+ ATF_REQUIRE(r == 0);
+ r = wordexp("'`date`'", &we, WRDE_NOCMD|WRDE_REUSE);
+ ATF_REQUIRE(r == 0);
+ r = wordexp("'$(date)'", &we, WRDE_NOCMD|WRDE_REUSE);
+ ATF_REQUIRE(r == 0);
+ wordfree(&we);
+}
+
+ATF_TC_WITHOUT_HEAD(WRDE_BADCHAR_test);
+ATF_TC_BODY(WRDE_BADCHAR_test, tc)
+{
+ wordexp_t we;
+ int r;
+
+ r = wordexp("'\n|&;<>(){}'", &we, 0);
+ ATF_REQUIRE(r == 0);
+ r = wordexp("\"\n|&;<>(){}\"", &we, WRDE_REUSE);
+ ATF_REQUIRE(r == 0);
+ r = wordexp("\\\n\\|\\&\\;\\<\\>\\(\\)\\{\\}", &we, WRDE_REUSE);
+ ATF_REQUIRE(r == 0);
+ wordfree(&we);
+ r = wordexp("test \n test", &we, 0);
+ ATF_REQUIRE(r == WRDE_BADCHAR);
+ r = wordexp("test | test", &we, 0);
+ ATF_REQUIRE(r == WRDE_BADCHAR);
+ r = wordexp("test & test", &we, 0);
+ ATF_REQUIRE(r == WRDE_BADCHAR);
+ r = wordexp("test ; test", &we, 0);
+ ATF_REQUIRE(r == WRDE_BADCHAR);
+ r = wordexp("test > test", &we, 0);
+ ATF_REQUIRE(r == WRDE_BADCHAR);
+ r = wordexp("test < test", &we, 0);
+ ATF_REQUIRE(r == WRDE_BADCHAR);
+ r = wordexp("test ( test", &we, 0);
+ ATF_REQUIRE(r == WRDE_BADCHAR);
+ r = wordexp("test ) test", &we, 0);
+ ATF_REQUIRE(r == WRDE_BADCHAR);
+ r = wordexp("test { test", &we, 0);
+ ATF_REQUIRE(r == WRDE_BADCHAR);
+ r = wordexp("test } test", &we, 0);
+ ATF_REQUIRE(r == WRDE_BADCHAR);
+}
+
+ATF_TC_WITHOUT_HEAD(WRDE_SYNTAX_test);
+ATF_TC_BODY(WRDE_SYNTAX_test, tc)
+{
+ wordexp_t we;
+ int r;
+
+ r = wordexp("'", &we, 0);
+ ATF_REQUIRE(r == WRDE_SYNTAX);
+ r = wordexp("'", &we, WRDE_UNDEF);
+ ATF_REQUIRE(r == WRDE_SYNTAX);
+ r = wordexp("'\\'", &we, 0);
+ ATF_REQUIRE(r == 0);
+ ATF_REQUIRE(we.we_wordc == 1);
+ ATF_REQUIRE(strcmp(we.we_wordv[0], "\\") == 0);
+ ATF_REQUIRE(we.we_wordv[1] == NULL);
+ wordfree(&we);
+ /* Two syntax errors that are not detected by the current we_check(). */
+ r = wordexp("${IFS:+'}", &we, 0);
+ ATF_REQUIRE(r == WRDE_SYNTAX);
+ r = wordexp("${IFS:+'}", &we, WRDE_UNDEF);
+ ATF_REQUIRE(r == WRDE_SYNTAX);
+ r = wordexp("$(case)", &we, 0);
+ ATF_REQUIRE(r == WRDE_SYNTAX);
+ r = wordexp("$(case)", &we, WRDE_UNDEF);
+ ATF_REQUIRE(r == WRDE_SYNTAX);
+}
+
+ATF_TC_WITHOUT_HEAD(with_SIGCHILD_handler_test);
+ATF_TC_BODY(with_SIGCHILD_handler_test, tc)
+{
+ struct sigaction sa;
+ wordexp_t we;
+ int r;
+
+ /* With a SIGCHLD handler that reaps all zombies. */
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = chld_handler;
+ r = sigaction(SIGCHLD, &sa, NULL);
+ ATF_REQUIRE(r == 0);
+ r = wordexp("hello world", &we, 0);
+ ATF_REQUIRE(r == 0);
+ ATF_REQUIRE(we.we_wordc == 2);
+ ATF_REQUIRE(strcmp(we.we_wordv[0], "hello") == 0);
+ ATF_REQUIRE(strcmp(we.we_wordv[1], "world") == 0);
+ ATF_REQUIRE(we.we_wordv[2] == NULL);
+ wordfree(&we);
+ sa.sa_handler = SIG_DFL;
+ r = sigaction(SIGCHLD, &sa, NULL);
+ ATF_REQUIRE(r == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(with_SIGCHILD_ignore_test);
+ATF_TC_BODY(with_SIGCHILD_ignore_test, tc)
+{
+ struct sigaction sa;
+ wordexp_t we;
+ int r;
+
+ /* With SIGCHLD set to ignore so that the kernel auto-reaps zombies. */
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = SIG_IGN;
+ r = sigaction(SIGCHLD, &sa, NULL);
+ ATF_REQUIRE(r == 0);
+ r = wordexp("hello world", &we, 0);
+ ATF_REQUIRE(r == 0);
+ ATF_REQUIRE(we.we_wordc == 2);
+ ATF_REQUIRE(strcmp(we.we_wordv[0], "hello") == 0);
+ ATF_REQUIRE(strcmp(we.we_wordv[1], "world") == 0);
+ ATF_REQUIRE(we.we_wordv[2] == NULL);
+ wordfree(&we);
+ sa.sa_handler = SIG_DFL;
+ r = sigaction(SIGCHLD, &sa, NULL);
+ ATF_REQUIRE(r == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(with_unused_non_default_IFS_test);
+ATF_TC_BODY(with_unused_non_default_IFS_test, tc)
+{
+ wordexp_t we;
+ int r;
+
+ /*
+ * With IFS set to a non-default value (without depending on whether
+ * IFS is inherited or not).
+ */
+ r = setenv("IFS", ":", 1);
+ ATF_REQUIRE(r == 0);
+ r = wordexp("hello world", &we, 0);
+ ATF_REQUIRE(r == 0);
+ ATF_REQUIRE(we.we_wordc == 2);
+ ATF_REQUIRE(strcmp(we.we_wordv[0], "hello") == 0);
+ ATF_REQUIRE(strcmp(we.we_wordv[1], "world") == 0);
+ ATF_REQUIRE(we.we_wordv[2] == NULL);
+ wordfree(&we);
+ r = unsetenv("IFS");
+ ATF_REQUIRE(r == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(with_used_non_default_IFS_test);
+ATF_TC_BODY(with_used_non_default_IFS_test, tc)
+{
+ wordexp_t we;
+ int r;
+
+ /*
+ * With IFS set to a non-default value, and using it.
+ */
+ r = setenv("IFS", ":", 1);
+ ATF_REQUIRE(r == 0);
+ r = wordexp("${IFS+hello:world}", &we, 0);
+ ATF_REQUIRE(r == 0);
+ ATF_REQUIRE(we.we_wordc == 2);
+ ATF_REQUIRE(strcmp(we.we_wordv[0], "hello") == 0);
+ ATF_REQUIRE(strcmp(we.we_wordv[1], "world") == 0);
+ ATF_REQUIRE(we.we_wordv[2] == NULL);
+ wordfree(&we);
+ r = unsetenv("IFS");
+ ATF_REQUIRE(r == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, simple_test);
+ ATF_TP_ADD_TC(tp, long_output_test);
+ ATF_TP_ADD_TC(tp, WRDE_DOOFFS_test);
+ ATF_TP_ADD_TC(tp, WRDE_REUSE_test);
+ ATF_TP_ADD_TC(tp, WRDE_APPEND_test);
+ ATF_TP_ADD_TC(tp, WRDE_DOOFFS__WRDE_APPEND_test);
+ ATF_TP_ADD_TC(tp, WRDE_UNDEF_test);
+ ATF_TP_ADD_TC(tp, WRDE_NOCMD_test);
+ ATF_TP_ADD_TC(tp, WRDE_BADCHAR_test);
+ ATF_TP_ADD_TC(tp, WRDE_SYNTAX_test);
+ ATF_TP_ADD_TC(tp, with_SIGCHILD_handler_test);
+ ATF_TP_ADD_TC(tp, with_SIGCHILD_ignore_test);
+ ATF_TP_ADD_TC(tp, with_unused_non_default_IFS_test);
+ ATF_TP_ADD_TC(tp, with_used_non_default_IFS_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/hash/Makefile b/lib/libc/tests/hash/Makefile
new file mode 100644
index 000000000000..d09d87b7cd8b
--- /dev/null
+++ b/lib/libc/tests/hash/Makefile
@@ -0,0 +1,37 @@
+PACKAGE= tests
+
+.include <src.opts.mk>
+
+NETBSD_ATF_TESTS_C=
+
+.if ${MK_OPENSSL} != "no"
+# XXX: doesn't compile
+#NETBSD_ATF_TESTS_C+= hmac_test
+NETBSD_ATF_TESTS_C+= sha2_test
+.endif
+
+NETBSD_ATF_TESTS_SH= hash_test
+
+BINDIR= ${TESTSDIR}
+
+PROGS+= h_hash
+
+FILESGROUPS+= ${PACKAGE}DATA_FILES
+${PACKAGE}DATA_FILESPACKAGE= tests
+
+${PACKAGE}DATA_FILESDIR= ${TESTSDIR}/data
+
+${PACKAGE}DATA_FILES+= data/md5test-in
+${PACKAGE}DATA_FILES+= data/md5test-out
+${PACKAGE}DATA_FILES+= data/sha1test-in
+${PACKAGE}DATA_FILES+= data/sha1test-out
+${PACKAGE}DATA_FILES+= data/sha1test2-out
+
+LIBADD+= md
+LIBADD.sha2_test+= crypto
+
+CFLAGS.h_hash+= -I${SRCTOP}/lib/libnetbsd
+
+.include "../Makefile.netbsd-tests"
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/hash/Makefile.depend b/lib/libc/tests/hash/Makefile.depend
new file mode 100644
index 000000000000..c969d41de00c
--- /dev/null
+++ b/lib/libc/tests/hash/Makefile.depend
@@ -0,0 +1,20 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libmd \
+ lib/libnetbsd \
+ secure/lib/libcrypto \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/iconv/Makefile b/lib/libc/tests/iconv/Makefile
new file mode 100644
index 000000000000..dc2dc5a925b9
--- /dev/null
+++ b/lib/libc/tests/iconv/Makefile
@@ -0,0 +1,5 @@
+TESTSDIR= ${TESTSBASE}/lib/libc/iconv
+
+ATF_TESTS_C+= iconvctl_test
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/iconv/Makefile.depend b/lib/libc/tests/iconv/Makefile.depend
new file mode 100644
index 000000000000..1af0c88e099c
--- /dev/null
+++ b/lib/libc/tests/iconv/Makefile.depend
@@ -0,0 +1,17 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/iconv/iconvctl_test.c b/lib/libc/tests/iconv/iconvctl_test.c
new file mode 100644
index 000000000000..f534218cf294
--- /dev/null
+++ b/lib/libc/tests/iconv/iconvctl_test.c
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 2016 Eric van Gyzen
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this 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 <iconv.h>
+
+#include <atf-c.h>
+
+static void
+test_trivialp(const char *src, const char *dst, int expected)
+{
+ iconv_t ic;
+ int actual, status;
+
+ ic = iconv_open(dst, src);
+ ATF_REQUIRE(ic != (iconv_t)-1);
+
+ status = iconvctl(ic, ICONV_TRIVIALP, &actual);
+ ATF_REQUIRE(status == 0);
+
+ ATF_REQUIRE(actual == expected);
+
+ status = iconv_close(ic);
+ ATF_REQUIRE(status == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(iconvctl_trivialp_test);
+ATF_TC_BODY(iconvctl_trivialp_test, tc)
+{
+
+ test_trivialp("ISO-8859-1", "ISO-8859-1", 1);
+ test_trivialp("ISO-8859-1", "ISO-8859-15", 0);
+ test_trivialp("ISO-8859-15", "ISO-8859-1", 0);
+ test_trivialp("ISO-8859-15", "UTF-8", 0);
+ test_trivialp("UTF-8", "ASCII", 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, iconvctl_trivialp_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/inet/Makefile b/lib/libc/tests/inet/Makefile
new file mode 100644
index 000000000000..3ceadfb8868c
--- /dev/null
+++ b/lib/libc/tests/inet/Makefile
@@ -0,0 +1,8 @@
+.include <bsd.own.mk>
+
+NETBSD_ATF_TESTS_C+= inet_addr_test
+NETBSD_ATF_TESTS_C+= inet_network_test
+
+.include "../Makefile.netbsd-tests"
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/inet/Makefile.depend b/lib/libc/tests/inet/Makefile.depend
new file mode 100644
index 000000000000..d0eb7a0f9f74
--- /dev/null
+++ b/lib/libc/tests/inet/Makefile.depend
@@ -0,0 +1,19 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libnetbsd \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/locale/Makefile b/lib/libc/tests/locale/Makefile
new file mode 100644
index 000000000000..70905cc58d24
--- /dev/null
+++ b/lib/libc/tests/locale/Makefile
@@ -0,0 +1,44 @@
+.include <bsd.own.mk>
+
+ATF_TESTS_C+= btowc_test
+ATF_TESTS_C+= c16rtomb_test
+ATF_TESTS_C+= iswctype_test
+ATF_TESTS_C+= mblen_test
+ATF_TESTS_C+= mbrlen_test
+ATF_TESTS_C+= mbrtoc16_test
+ATF_TESTS_C+= mbrtowc_2_test
+ATF_TESTS_C+= mbsnrtowcs_2_test
+ATF_TESTS_C+= mbsrtowcs_test
+ATF_TESTS_C+= mbstowcs_2_test
+ATF_TESTS_C+= mbtowc_2_test
+ATF_TESTS_C+= newlocale_test
+ATF_TESTS_C+= towctrans_test
+ATF_TESTS_C+= wcrtomb_test
+ATF_TESTS_C+= wcsnrtombs_test
+ATF_TESTS_C+= wcsrtombs_test
+ATF_TESTS_C+= wcstombs_test
+ATF_TESTS_C+= wctomb_2_test
+
+# Note: io_test requires zh_TW.Big5 locale (see ^/head@r315568)
+#NETBSD_ATF_TESTS_C= io_test
+NETBSD_ATF_TESTS_C+= mbrtowc_test
+NETBSD_ATF_TESTS_C+= mbstowcs_test
+NETBSD_ATF_TESTS_C+= mbsnrtowcs_test
+NETBSD_ATF_TESTS_C+= mbtowc_test
+NETBSD_ATF_TESTS_C+= wcscspn_test
+NETBSD_ATF_TESTS_C+= wcspbrk_test
+NETBSD_ATF_TESTS_C+= wcsspn_test
+NETBSD_ATF_TESTS_C+= wcstod_test
+NETBSD_ATF_TESTS_C+= wctomb_test
+
+SRCS.mbrtowc_2_test= mbrtowc_test.c
+SRCS.mbsnrtowcs_2_test= mbsnrtowcs_test.c
+SRCS.mbstowcs_2_test= mbstowcs_test.c
+SRCS.mbtowc_2_test= mbtowc_test.c
+SRCS.wctomb_2_test= wctomb_test.c
+
+CFLAGS.t_wctomb.c+= -Wno-stack-protector
+
+.include "../Makefile.netbsd-tests"
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/locale/Makefile.depend b/lib/libc/tests/locale/Makefile.depend
new file mode 100644
index 000000000000..e5ddbc552be2
--- /dev/null
+++ b/lib/libc/tests/locale/Makefile.depend
@@ -0,0 +1,19 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libnetbsd \
+ lib/msun \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/locale/btowc_test.c b/lib/libc/tests/locale/btowc_test.c
new file mode 100644
index 000000000000..27e6ff533621
--- /dev/null
+++ b/lib/libc/tests/locale/btowc_test.c
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2002 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test program for btowc() and wctob() as specified by IEEE Std. 1003.1-2001
+ * and ISO/IEC 9899:1999.
+ *
+ * The function is tested in the "C" and "ja_JP.eucJP" locales.
+ */
+
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(btowc_test);
+ATF_TC_BODY(btowc_test, tc)
+{
+ int i;
+
+ /* C/POSIX locale. */
+ ATF_REQUIRE(btowc(EOF) == WEOF);
+ ATF_REQUIRE(wctob(WEOF) == EOF);
+ for (i = 0; i < UCHAR_MAX; i++)
+ ATF_REQUIRE(btowc(i) == (wchar_t)i && i == (int)wctob(i));
+
+ /* Japanese (EUC) locale. */
+ ATF_REQUIRE(strcmp(setlocale(LC_CTYPE, "ja_JP.eucJP"), "ja_JP.eucJP") == 0);
+ ATF_REQUIRE(MB_CUR_MAX > 1);
+ ATF_REQUIRE(btowc('A') == L'A' && wctob(L'A') == 'A');
+ ATF_REQUIRE(btowc(0xa3) == WEOF && wctob(0xa3c1) == EOF);
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, btowc_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/locale/c16rtomb_test.c b/lib/libc/tests/locale/c16rtomb_test.c
new file mode 100644
index 000000000000..6812fbbc5164
--- /dev/null
+++ b/lib/libc/tests/locale/c16rtomb_test.c
@@ -0,0 +1,163 @@
+/*-
+ * Copyright (c) 2002 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Copyright (c) 2013 Ed Schouten <ed@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Test program for c16rtomb() as specified by ISO/IEC 9899:2011.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <string.h>
+#include <uchar.h>
+
+#include <atf-c.h>
+
+static void
+require_lc_ctype(const char *locale_name)
+{
+ char *lc_ctype_set;
+
+ lc_ctype_set = setlocale(LC_CTYPE, locale_name);
+ if (lc_ctype_set == NULL)
+ atf_tc_fail("setlocale(LC_CTYPE, \"%s\") failed; errno=%d",
+ locale_name, errno);
+
+ ATF_REQUIRE(strcmp(lc_ctype_set, locale_name) == 0);
+}
+
+static mbstate_t s;
+static char buf[MB_LEN_MAX + 1];
+
+ATF_TC_WITHOUT_HEAD(c16rtomb_c_locale_test);
+ATF_TC_BODY(c16rtomb_c_locale_test, tc)
+{
+
+ require_lc_ctype("C");
+
+ /*
+ * If the buffer argument is NULL, c16 is implicitly 0,
+ * c16rtomb() resets its internal state.
+ */
+ ATF_REQUIRE(c16rtomb(NULL, L'\0', NULL) == 1);
+ ATF_REQUIRE(c16rtomb(NULL, 0xdc00, NULL) == 1);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_REQUIRE(c16rtomb(buf, 0, &s) == 1);
+ ATF_REQUIRE((unsigned char)buf[0] == 0 && (unsigned char)buf[1] == 0xcc);
+
+ /* Latin letter A, internal state. */
+ ATF_REQUIRE(c16rtomb(NULL, L'\0', NULL) == 1);
+ ATF_REQUIRE(c16rtomb(NULL, L'A', NULL) == 1);
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_REQUIRE(c16rtomb(buf, L'A', &s) == 1);
+ ATF_REQUIRE((unsigned char)buf[0] == 'A' && (unsigned char)buf[1] == 0xcc);
+
+ /* Unicode character 'Pile of poo'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_REQUIRE(c16rtomb(buf, 0xd83d, &s) == 0);
+ ATF_REQUIRE(c16rtomb(buf, 0xdca9, &s) == (size_t)-1);
+ ATF_REQUIRE(errno == EILSEQ);
+ ATF_REQUIRE((unsigned char)buf[0] == 0xcc);
+}
+
+ATF_TC_WITHOUT_HEAD(c16rtomb_iso_8859_1_test);
+ATF_TC_BODY(c16rtomb_iso_8859_1_test, tc)
+{
+
+ require_lc_ctype("en_US.ISO8859-1");
+
+ /* Unicode character 'Euro sign'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_REQUIRE(c16rtomb(buf, 0x20ac, &s) == (size_t)-1);
+ ATF_REQUIRE(errno == EILSEQ);
+ ATF_REQUIRE((unsigned char)buf[0] == 0xcc);
+}
+
+ATF_TC_WITHOUT_HEAD(c16rtomb_iso_8859_15_test);
+ATF_TC_BODY(c16rtomb_iso_8859_15_test, tc)
+{
+
+ require_lc_ctype("en_US.ISO8859-15");
+
+ /* Unicode character 'Euro sign'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_REQUIRE(c16rtomb(buf, 0x20ac, &s) == 1);
+ ATF_REQUIRE((unsigned char)buf[0] == 0xa4 && (unsigned char)buf[1] == 0xcc);
+}
+
+ATF_TC_WITHOUT_HEAD(c16rtomb_utf_8_test);
+ATF_TC_BODY(c16rtomb_utf_8_test, tc)
+{
+
+ require_lc_ctype("en_US.UTF-8");
+
+ /* Unicode character 'Pile of poo'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_REQUIRE(c16rtomb(buf, 0xd83d, &s) == 0);
+ ATF_REQUIRE(c16rtomb(buf, 0xdca9, &s) == 4);
+ ATF_REQUIRE((unsigned char)buf[0] == 0xf0 && (unsigned char)buf[1] == 0x9f &&
+ (unsigned char)buf[2] == 0x92 && (unsigned char)buf[3] == 0xa9 &&
+ (unsigned char)buf[4] == 0xcc);
+
+ /* Invalid code; 'Pile of poo' without the trail surrogate. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_REQUIRE(c16rtomb(buf, 0xd83d, &s) == 0);
+ ATF_REQUIRE(c16rtomb(buf, L'A', &s) == (size_t)-1);
+ ATF_REQUIRE(errno == EILSEQ);
+ ATF_REQUIRE((unsigned char)buf[0] == 0xcc);
+
+ /* Invalid code; 'Pile of poo' without the lead surrogate. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_REQUIRE(c16rtomb(buf, 0xdca9, &s) == (size_t)-1);
+ ATF_REQUIRE(errno == EILSEQ);
+ ATF_REQUIRE((unsigned char)buf[0] == 0xcc);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, c16rtomb_c_locale_test);
+ ATF_TP_ADD_TC(tp, c16rtomb_iso_8859_1_test);
+ ATF_TP_ADD_TC(tp, c16rtomb_iso_8859_15_test);
+ ATF_TP_ADD_TC(tp, c16rtomb_utf_8_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/locale/iswctype_test.c b/lib/libc/tests/locale/iswctype_test.c
new file mode 100644
index 000000000000..79b941522d2b
--- /dev/null
+++ b/lib/libc/tests/locale/iswctype_test.c
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 2003 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test program for wctype() and iswctype() as specified by
+ * IEEE Std. 1003.1-2001 and ISO/IEC 9899:1999.
+ *
+ * The functions are tested in the "C" and "ja_JP.eucJP" locales.
+ */
+
+#include <sys/param.h>
+#include <errno.h>
+#include <locale.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include <atf-c.h>
+
+static void
+require_lc_ctype(const char *locale_name)
+{
+ char *lc_ctype_set;
+
+ lc_ctype_set = setlocale(LC_CTYPE, locale_name);
+ if (lc_ctype_set == NULL)
+ atf_tc_fail("setlocale(LC_CTYPE, \"%s\") failed; errno=%d",
+ locale_name, errno);
+
+ ATF_REQUIRE(strcmp(lc_ctype_set, locale_name) == 0);
+}
+
+static wctype_t t;
+static int i, j;
+static struct {
+ const char *name;
+ int (*func)(wint_t);
+} cls[] = {
+ { "alnum", iswalnum },
+ { "alpha", iswalpha },
+ { "blank", iswblank },
+ { "cntrl", iswcntrl },
+ { "digit", iswdigit },
+ { "graph", iswgraph },
+ { "lower", iswlower },
+ { "print", iswprint },
+ { "punct", iswpunct },
+ { "space", iswspace },
+ { "upper", iswupper },
+ { "xdigit", iswxdigit }
+};
+
+ATF_TC_WITHOUT_HEAD(iswctype_c_locale_test);
+ATF_TC_BODY(iswctype_c_locale_test, tc)
+{
+
+ require_lc_ctype("C");
+ for (i = 0; i < nitems(cls); i++) {
+ t = wctype(cls[i].name);
+ ATF_REQUIRE(t != 0);
+ for (j = 0; j < 256; j++)
+ ATF_REQUIRE(cls[i].func(j) == iswctype(j, t));
+ }
+ t = wctype("elephant");
+ ATF_REQUIRE(t == 0);
+ for (i = 0; i < 256; i++)
+ ATF_REQUIRE(iswctype(i, t) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(iswctype_euc_jp_test);
+ATF_TC_BODY(iswctype_euc_jp_test, tc)
+{
+
+ require_lc_ctype("ja_JP.eucJP");
+
+ for (i = 0; i < nitems(cls); i++) {
+ t = wctype(cls[i].name);
+ ATF_REQUIRE(t != 0);
+ for (j = 0; j < 65536; j++)
+ ATF_REQUIRE(cls[i].func(j) == iswctype(j, t));
+ }
+ t = wctype("elephant");
+ ATF_REQUIRE(t == 0);
+ for (i = 0; i < 65536; i++)
+ ATF_REQUIRE(iswctype(i, t) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, iswctype_c_locale_test);
+ ATF_TP_ADD_TC(tp, iswctype_euc_jp_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/locale/mblen_test.c b/lib/libc/tests/locale/mblen_test.c
new file mode 100644
index 000000000000..e081b0d4246a
--- /dev/null
+++ b/lib/libc/tests/locale/mblen_test.c
@@ -0,0 +1,113 @@
+/*-
+ * Copyright (c) 2002-2004 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test program for mblen(), as specified by IEEE Std. 1003.1-2001 and
+ * ISO/IEC 9899:1990.
+ *
+ * The function is tested with both the "C" ("POSIX") LC_CTYPE setting and
+ * "ja_JP.eucJP". Other encodings are not tested.
+ */
+
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(mblen_test);
+ATF_TC_BODY(mblen_test, tc)
+{
+ char buf[MB_LEN_MAX + 1];
+
+ /*
+ * C/POSIX locale.
+ */
+
+ ATF_REQUIRE(MB_CUR_MAX == 1);
+
+ /* No shift states in C locale. */
+ ATF_REQUIRE(mblen(NULL, 0) == 0);
+
+ /* Null wide character. */
+ memset(buf, 0xcc, sizeof(buf));
+ buf[0] = '\0';
+ ATF_REQUIRE(mblen(buf, 1) == 0);
+
+ /* Latin letter A. */
+ buf[0] = 'A';
+ ATF_REQUIRE(mblen(buf, 1) == 1);
+
+ /* Incomplete character sequence. */
+ buf[0] = '\0';
+ ATF_REQUIRE(mblen(buf, 0) == -1);
+ ATF_REQUIRE(mblen(NULL, 0) == 0);
+
+ /*
+ * Japanese (EUC) locale.
+ */
+
+ ATF_REQUIRE(strcmp(setlocale(LC_CTYPE, "ja_JP.eucJP"), "ja_JP.eucJP") == 0);
+ ATF_REQUIRE(MB_CUR_MAX > 1);
+
+ /* No shift states in EUC. */
+ ATF_REQUIRE(mblen(NULL, 0) == 0);
+
+ /* Null wide character. */
+ memset(buf, 0xcc, sizeof(buf));
+ buf[0] = '\0';
+ ATF_REQUIRE(mblen(buf, 1) == 0);
+
+ /* Latin letter A. */
+ buf[0] = 'A';
+ ATF_REQUIRE(mblen(buf, 1) == 1);
+
+ /* Incomplete character sequence. */
+ buf[0] = '\0';
+ ATF_REQUIRE(mblen(buf, 0) == -1);
+ ATF_REQUIRE(mblen(NULL, 0) == 0);
+
+ /* Incomplete character sequence (truncated double-byte). */
+ memset(buf, 0xcc, sizeof(buf));
+ buf[0] = 0xa3;
+ buf[1] = 0x00;
+ ATF_REQUIRE(mblen(buf, 1) == -1);
+ ATF_REQUIRE(mblen(NULL, 0) == 0);
+
+ /* Same as above, but complete. */
+ buf[1] = 0xc1;
+ ATF_REQUIRE(mblen(buf, 2) == 2);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mblen_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/locale/mbrlen_test.c b/lib/libc/tests/locale/mbrlen_test.c
new file mode 100644
index 000000000000..50088596fc89
--- /dev/null
+++ b/lib/libc/tests/locale/mbrlen_test.c
@@ -0,0 +1,123 @@
+/*-
+ * Copyright (c) 2002 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test program for mbrlen(), as specified by IEEE Std. 1003.1-2001 and
+ * ISO/IEC 9899:1999.
+ *
+ * The function is tested with both the "C" ("POSIX") LC_CTYPE setting and
+ * "ja_JP.eucJP". Other encodings are not tested.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(mbrlen_test);
+ATF_TC_BODY(mbrlen_test, tc)
+{
+ mbstate_t s;
+ char buf[MB_LEN_MAX + 1];
+
+ /* C/POSIX locale. */
+ ATF_REQUIRE(MB_CUR_MAX == 1);
+
+ /* Null wide character, internal state. */
+ memset(buf, 0xcc, sizeof(buf));
+ buf[0] = 0;
+ ATF_REQUIRE(mbrlen(buf, 1, NULL) == 0);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrlen(buf, 1, &s) == 0);
+
+ /* Latin letter A, internal state. */
+ ATF_REQUIRE(mbrlen(NULL, 0, NULL) == 0);
+ buf[0] = 'A';
+ ATF_REQUIRE(mbrlen(buf, 1, NULL) == 1);
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrlen(buf, 1, &s) == 1);
+
+ /* Incomplete character sequence. */
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrlen(buf, 0, &s) == (size_t)-2);
+
+ /* Japanese (EUC) locale. */
+
+ ATF_REQUIRE(strcmp(setlocale(LC_CTYPE, "ja_JP.eucJP"), "ja_JP.eucJP") == 0);
+ ATF_REQUIRE(MB_CUR_MAX > 1);
+
+ /* Null wide character, internal state. */
+ ATF_REQUIRE(mbrlen(NULL, 0, NULL) == 0);
+ memset(buf, 0xcc, sizeof(buf));
+ buf[0] = 0;
+ ATF_REQUIRE(mbrlen(buf, 1, NULL) == 0);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrlen(buf, 1, &s) == 0);
+
+ /* Latin letter A, internal state. */
+ ATF_REQUIRE(mbrlen(NULL, 0, NULL) == 0);
+ buf[0] = 'A';
+ ATF_REQUIRE(mbrlen(buf, 1, NULL) == 1);
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrlen(buf, 1, &s) == 1);
+
+ /* Incomplete character sequence (zero length). */
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrlen(buf, 0, &s) == (size_t)-2);
+
+ /* Incomplete character sequence (truncated double-byte). */
+ memset(buf, 0xcc, sizeof(buf));
+ buf[0] = 0xa3;
+ buf[1] = 0x00;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrlen(buf, 1, &s) == (size_t)-2);
+
+ /* Same as above, but complete. */
+ buf[1] = 0xc1;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrlen(buf, 2, &s) == 2);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mbrlen_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/locale/mbrtoc16_test.c b/lib/libc/tests/locale/mbrtoc16_test.c
new file mode 100644
index 000000000000..98a140c56fc0
--- /dev/null
+++ b/lib/libc/tests/locale/mbrtoc16_test.c
@@ -0,0 +1,211 @@
+/*-
+ * Copyright (c) 2002 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Copyright (c) 2013 Ed Schouten <ed@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * Test program for mbrtoc16() as specified by ISO/IEC 9899:2011.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <string.h>
+#include <uchar.h>
+
+#include <atf-c.h>
+
+static void
+require_lc_ctype(const char *locale_name)
+{
+ char *lc_ctype_set;
+
+ lc_ctype_set = setlocale(LC_CTYPE, locale_name);
+ if (lc_ctype_set == NULL)
+ atf_tc_fail("setlocale(LC_CTYPE, \"%s\") failed; errno=%d",
+ locale_name, errno);
+
+ ATF_REQUIRE(strcmp(lc_ctype_set, locale_name) == 0);
+}
+
+static mbstate_t s;
+static char16_t c16;
+
+ATF_TC_WITHOUT_HEAD(mbrtoc16_c_locale_test);
+ATF_TC_BODY(mbrtoc16_c_locale_test, tc)
+{
+
+ require_lc_ctype("C");
+
+ /* Null wide character, internal state. */
+ ATF_REQUIRE(mbrtoc16(&c16, "", 1, NULL) == 0);
+ ATF_REQUIRE(c16 == 0);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrtoc16(&c16, "", 1, &s) == 0);
+ ATF_REQUIRE(c16 == 0);
+
+ /* Latin letter A, internal state. */
+ ATF_REQUIRE(mbrtoc16(NULL, 0, 0, NULL) == 0);
+ ATF_REQUIRE(mbrtoc16(&c16, "A", 1, NULL) == 1);
+ ATF_REQUIRE(c16 == L'A');
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrtoc16(&c16, "A", 1, &s) == 1);
+ ATF_REQUIRE(c16 == L'A');
+
+ /* Incomplete character sequence. */
+ c16 = L'z';
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrtoc16(&c16, "", 0, &s) == (size_t)-2);
+ ATF_REQUIRE(c16 == L'z');
+
+ /* Check that mbrtoc16() doesn't access the buffer when n == 0. */
+ c16 = L'z';
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrtoc16(&c16, "", 0, &s) == (size_t)-2);
+ ATF_REQUIRE(c16 == L'z');
+
+ /* Check that mbrtoc16() doesn't read ahead too aggressively. */
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrtoc16(&c16, "AB", 2, &s) == 1);
+ ATF_REQUIRE(c16 == L'A');
+ ATF_REQUIRE(mbrtoc16(&c16, "C", 1, &s) == 1);
+ ATF_REQUIRE(c16 == L'C');
+
+}
+
+ATF_TC_WITHOUT_HEAD(mbrtoc16_iso_8859_1_test);
+ATF_TC_BODY(mbrtoc16_iso_8859_1_test, tc)
+{
+
+ require_lc_ctype("en_US.ISO8859-1");
+
+ /* Currency sign. */
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrtoc16(&c16, "\xa4", 1, &s) == 1);
+ ATF_REQUIRE(c16 == 0xa4);
+}
+
+ATF_TC_WITHOUT_HEAD(mbrtoc16_iso_8859_15_test);
+ATF_TC_BODY(mbrtoc16_iso_8859_15_test, tc)
+{
+
+ require_lc_ctype("en_US.ISO8859-15");
+
+ /* Euro sign. */
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrtoc16(&c16, "\xa4", 1, &s) == 1);
+ ATF_REQUIRE(c16 == 0x20ac);
+}
+
+ATF_TC_WITHOUT_HEAD(mbrtoc16_utf_8_test);
+ATF_TC_BODY(mbrtoc16_utf_8_test, tc)
+{
+
+ require_lc_ctype("en_US.UTF-8");
+
+ /* Null wide character, internal state. */
+ ATF_REQUIRE(mbrtoc16(NULL, 0, 0, NULL) == 0);
+ ATF_REQUIRE(mbrtoc16(&c16, "", 1, NULL) == 0);
+ ATF_REQUIRE(c16 == 0);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrtoc16(&c16, "", 1, &s) == 0);
+ ATF_REQUIRE(c16 == 0);
+
+ /* Latin letter A, internal state. */
+ ATF_REQUIRE(mbrtoc16(NULL, 0, 0, NULL) == 0);
+ ATF_REQUIRE(mbrtoc16(&c16, "A", 1, NULL) == 1);
+ ATF_REQUIRE(c16 == L'A');
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrtoc16(&c16, "A", 1, &s) == 1);
+ ATF_REQUIRE(c16 == L'A');
+
+ /* Incomplete character sequence (zero length). */
+ c16 = L'z';
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrtoc16(&c16, "", 0, &s) == (size_t)-2);
+ ATF_REQUIRE(c16 == L'z');
+
+ /* Incomplete character sequence (truncated double-byte). */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_REQUIRE(mbrtoc16(&c16, "\xc3", 1, &s) == (size_t)-2);
+
+ /* Same as above, but complete. */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_REQUIRE(mbrtoc16(&c16, "\xc3\x84", 2, &s) == 2);
+ ATF_REQUIRE(c16 == 0xc4);
+
+ /* Test restarting behaviour. */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_REQUIRE(mbrtoc16(&c16, "\xc3", 1, &s) == (size_t)-2);
+ ATF_REQUIRE(c16 == 0);
+ ATF_REQUIRE(mbrtoc16(&c16, "\xb7", 1, &s) == 1);
+ ATF_REQUIRE(c16 == 0xf7);
+
+ /* Surrogate pair. */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_REQUIRE(mbrtoc16(&c16, "\xf0\x9f\x92\xa9", 4, &s) == 4);
+ ATF_REQUIRE(c16 == 0xd83d);
+ ATF_REQUIRE(mbrtoc16(&c16, "", 0, &s) == (size_t)-3);
+ ATF_REQUIRE(c16 == 0xdca9);
+
+ /* Letter e with acute, precomposed. */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_REQUIRE(mbrtoc16(&c16, "\xc3\xa9", 2, &s) == 2);
+ ATF_REQUIRE(c16 == 0xe9);
+
+ /* Letter e with acute, combined. */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_REQUIRE(mbrtoc16(&c16, "\x65\xcc\x81", 3, &s) == 1);
+ ATF_REQUIRE(c16 == 0x65);
+ ATF_REQUIRE(mbrtoc16(&c16, "\xcc\x81", 2, &s) == 2);
+ ATF_REQUIRE(c16 == 0x301);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mbrtoc16_c_locale_test);
+ ATF_TP_ADD_TC(tp, mbrtoc16_iso_8859_1_test);
+ ATF_TP_ADD_TC(tp, mbrtoc16_iso_8859_15_test);
+ ATF_TP_ADD_TC(tp, mbrtoc16_utf_8_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/locale/mbrtowc_test.c b/lib/libc/tests/locale/mbrtowc_test.c
new file mode 100644
index 000000000000..df611a7df573
--- /dev/null
+++ b/lib/libc/tests/locale/mbrtowc_test.c
@@ -0,0 +1,162 @@
+/*-
+ * Copyright (c) 2002 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test program for mbrtowc(), as specified by IEEE Std. 1003.1-2001 and
+ * ISO/IEC 9899:1999.
+ *
+ * The function is tested with both the "C" ("POSIX") LC_CTYPE setting and
+ * "ja_JP.eucJP". Other encodings are not tested.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(mbrtowc_test);
+ATF_TC_BODY(mbrtowc_test, tc)
+{
+ mbstate_t s;
+ wchar_t wc;
+ char buf[MB_LEN_MAX + 1];
+
+ /*
+ * C/POSIX locale.
+ */
+
+ ATF_REQUIRE(MB_CUR_MAX == 1);
+
+ /* Null wide character, internal state. */
+ memset(buf, 0xcc, sizeof(buf));
+ buf[0] = 0;
+ ATF_REQUIRE(mbrtowc(&wc, buf, 1, NULL) == 0);
+ ATF_REQUIRE(wc == 0);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrtowc(&wc, buf, 1, &s) == 0);
+ ATF_REQUIRE(wc == 0);
+
+ /* Latin letter A, internal state. */
+ ATF_REQUIRE(mbrtowc(NULL, 0, 0, NULL) == 0);
+ buf[0] = 'A';
+ ATF_REQUIRE(mbrtowc(&wc, buf, 1, NULL) == 1);
+ ATF_REQUIRE(wc == L'A');
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrtowc(&wc, buf, 1, &s) == 1);
+ ATF_REQUIRE(wc == L'A');
+
+ /* Incomplete character sequence. */
+ wc = L'z';
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrtowc(&wc, buf, 0, &s) == (size_t)-2);
+ ATF_REQUIRE(wc == L'z');
+
+ /* Check that mbrtowc() doesn't access the buffer when n == 0. */
+ wc = L'z';
+ memset(&s, 0, sizeof(s));
+ buf[0] = '\0';
+ ATF_REQUIRE(mbrtowc(&wc, buf, 0, &s) == (size_t)-2);
+ ATF_REQUIRE(wc == L'z');
+
+ /*
+ * Japanese (EUC) locale.
+ */
+
+ ATF_REQUIRE(strcmp(setlocale(LC_CTYPE, "ja_JP.eucJP"), "ja_JP.eucJP") == 0);
+ ATF_REQUIRE(MB_CUR_MAX > 1);
+
+ /* Null wide character, internal state. */
+ ATF_REQUIRE(mbrtowc(NULL, 0, 0, NULL) == 0);
+ memset(buf, 0xcc, sizeof(buf));
+ buf[0] = 0;
+ ATF_REQUIRE(mbrtowc(&wc, buf, 1, NULL) == 0);
+ ATF_REQUIRE(wc == 0);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrtowc(&wc, buf, 1, &s) == 0);
+ ATF_REQUIRE(wc == 0);
+
+ /* Latin letter A, internal state. */
+ ATF_REQUIRE(mbrtowc(NULL, 0, 0, NULL) == 0);
+ buf[0] = 'A';
+ ATF_REQUIRE(mbrtowc(&wc, buf, 1, NULL) == 1);
+ ATF_REQUIRE(wc == L'A');
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrtowc(&wc, buf, 1, &s) == 1);
+ ATF_REQUIRE(wc == L'A');
+
+ /* Incomplete character sequence (zero length). */
+ wc = L'z';
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbrtowc(&wc, buf, 0, &s) == (size_t)-2);
+ ATF_REQUIRE(wc == L'z');
+
+ /* Incomplete character sequence (truncated double-byte). */
+ memset(buf, 0xcc, sizeof(buf));
+ buf[0] = 0xa3;
+ buf[1] = 0x00;
+ memset(&s, 0, sizeof(s));
+ wc = 0;
+ ATF_REQUIRE(mbrtowc(&wc, buf, 1, &s) == (size_t)-2);
+
+ /* Same as above, but complete. */
+ buf[1] = 0xc1;
+ memset(&s, 0, sizeof(s));
+ wc = 0;
+ ATF_REQUIRE(mbrtowc(&wc, buf, 2, &s) == 2);
+ ATF_REQUIRE(wc == 0xa3c1);
+
+ /* Test restarting behaviour. */
+ memset(buf, 0xcc, sizeof(buf));
+ buf[0] = 0xa3;
+ memset(&s, 0, sizeof(s));
+ wc = 0;
+ ATF_REQUIRE(mbrtowc(&wc, buf, 1, &s) == (size_t)-2);
+ ATF_REQUIRE(wc == 0);
+ buf[0] = 0xc1;
+ ATF_REQUIRE(mbrtowc(&wc, buf, 1, &s) == 1);
+ ATF_REQUIRE(wc == 0xa3c1);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mbrtowc_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/locale/mbsnrtowcs_test.c b/lib/libc/tests/locale/mbsnrtowcs_test.c
new file mode 100644
index 000000000000..a355bc7d7a6a
--- /dev/null
+++ b/lib/libc/tests/locale/mbsnrtowcs_test.c
@@ -0,0 +1,192 @@
+/*-
+ * Copyright (c) 2002-2004 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test program for mbsnrtowcs().
+ *
+ * The function is tested with both the "C" ("POSIX") LC_CTYPE setting and
+ * "ja_JP.eucJP". Other encodings are not tested.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(mbsnrtowcs_test);
+ATF_TC_BODY(mbsnrtowcs_test, tc)
+{
+ char srcbuf[128];
+ wchar_t dstbuf[128];
+ char *src;
+ mbstate_t s;
+
+ /* C/POSIX locale. */
+
+ /* Simple null terminated string. */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "hello");
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbsnrtowcs(dstbuf, (const char **)&src, 6, sizeof(dstbuf) /
+ sizeof(*dstbuf), &s) == 5);
+ ATF_REQUIRE(wcscmp(dstbuf, L"hello") == 0);
+ ATF_REQUIRE(dstbuf[6] == 0xcccc);
+ ATF_REQUIRE(src == NULL);
+
+ /* Simple null terminated string, stopping early. */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "hello");
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbsnrtowcs(dstbuf, (const char **)&src, 4, sizeof(dstbuf) /
+ sizeof(*dstbuf), &s) == 4);
+ ATF_REQUIRE(wmemcmp(dstbuf, L"hell", 4) == 0);
+ ATF_REQUIRE(dstbuf[5] == 0xcccc);
+ ATF_REQUIRE(src == srcbuf + 4);
+
+ /* Not enough space in destination buffer. */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "hello");
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbsnrtowcs(dstbuf, (const char **)&src, 6, 4, &s) == 4);
+ ATF_REQUIRE(wmemcmp(dstbuf, L"hell", 4) == 0);
+ ATF_REQUIRE(dstbuf[5] == 0xcccc);
+ ATF_REQUIRE(src == srcbuf + 4);
+
+ /* Null terminated string, internal dest. buffer */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "hello");
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbsnrtowcs(NULL, (const char **)&src, 6, 0, &s) == 5);
+
+ /* Null terminated string, internal dest. buffer, stopping early */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "hello");
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbsnrtowcs(NULL, (const char **)&src, 4, 0, &s) == 4);
+
+ /* Null terminated string, internal state. */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "hello");
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ src = srcbuf;
+ ATF_REQUIRE(mbsnrtowcs(dstbuf, (const char **)&src, 6, sizeof(dstbuf) /
+ sizeof(*dstbuf), NULL) == 5);
+ ATF_REQUIRE(wcscmp(dstbuf, L"hello") == 0);
+ ATF_REQUIRE(dstbuf[6] == 0xcccc);
+ ATF_REQUIRE(src == NULL);
+
+ /* Null terminated string, internal state, internal dest. buffer. */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "hello");
+ src = srcbuf;
+ ATF_REQUIRE(mbsnrtowcs(NULL, (const char **)&src, 6, 0, NULL) == 5);
+
+ /* Empty source buffer. */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ srcbuf[0] = '\0';
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ ATF_REQUIRE(mbsnrtowcs(dstbuf, (const char **)&src, 1, 1, &s) == 0);
+ ATF_REQUIRE(dstbuf[0] == 0);
+ ATF_REQUIRE(dstbuf[1] == 0xcccc);
+ ATF_REQUIRE(src == NULL);
+
+ /* Zero length destination buffer. */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "hello");
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ ATF_REQUIRE(mbsnrtowcs(dstbuf, (const char **)&src, 1, 0, &s) == 0);
+ ATF_REQUIRE(dstbuf[0] == 0xcccc);
+ ATF_REQUIRE(src == srcbuf);
+
+ /* Zero length source buffer. */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ ATF_REQUIRE(mbsnrtowcs(dstbuf, (const char **)&src, 0, 1, &s) == 0);
+ ATF_REQUIRE(dstbuf[0] == 0xcccc);
+ ATF_REQUIRE(src == srcbuf);
+
+ /*
+ * Japanese (EUC) locale.
+ */
+
+ ATF_REQUIRE(strcmp(setlocale(LC_CTYPE, "ja_JP.eucJP"), "ja_JP.eucJP") == 0);
+ ATF_REQUIRE(MB_CUR_MAX > 1);
+
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "\xA3\xC1 B \xA3\xC3");
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ ATF_REQUIRE(mbsnrtowcs(dstbuf, (const char **)&src, 8, sizeof(dstbuf) /
+ sizeof(*dstbuf), &s) == 5);
+ ATF_REQUIRE(dstbuf[0] == 0xA3C1 && dstbuf[1] == 0x20 && dstbuf[2] == 0x42 &&
+ dstbuf[3] == 0x20 && dstbuf[4] == 0xA3C3 && dstbuf[5] == 0);
+ ATF_REQUIRE(src == NULL);
+
+ /* Partial character. */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "\xA3\xC1 B \xA3\xC3");
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ ATF_REQUIRE(mbsnrtowcs(dstbuf, (const char **)&src, 6, sizeof(dstbuf) /
+ sizeof(*dstbuf), &s) == 4);
+ ATF_REQUIRE(src == srcbuf + 6);
+ ATF_REQUIRE(!mbsinit(&s));
+ ATF_REQUIRE(mbsnrtowcs(dstbuf, (const char **)&src, 1, sizeof(dstbuf) /
+ sizeof(*dstbuf), &s) == 1);
+ ATF_REQUIRE(src == srcbuf + 7);
+ ATF_REQUIRE(mbsnrtowcs(dstbuf, (const char **)&src, 1, sizeof(dstbuf) /
+ sizeof(*dstbuf), &s) == 0);
+ ATF_REQUIRE(src == NULL);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mbsnrtowcs_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/locale/mbsrtowcs_test.c b/lib/libc/tests/locale/mbsrtowcs_test.c
new file mode 100644
index 000000000000..d8d215fda2ad
--- /dev/null
+++ b/lib/libc/tests/locale/mbsrtowcs_test.c
@@ -0,0 +1,150 @@
+/*-
+ * Copyright (c) 2002 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test program for mbsrtowcs(), as specified by IEEE Std. 1003.1-2001 and
+ * ISO/IEC 9899:1999.
+ *
+ * The function is tested with both the "C" ("POSIX") LC_CTYPE setting and
+ * "ja_JP.eucJP". Other encodings are not tested.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(mbsrtowcs_test);
+ATF_TC_BODY(mbsrtowcs_test, tc)
+{
+ char srcbuf[128];
+ wchar_t dstbuf[128];
+ char *src;
+ mbstate_t s;
+
+ /*
+ * C/POSIX locale.
+ */
+
+ /* Simple null terminated string. */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "hello");
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbsrtowcs(dstbuf, (const char **)&src, sizeof(dstbuf) /
+ sizeof(*dstbuf), &s) == 5);
+ ATF_REQUIRE(wcscmp(dstbuf, L"hello") == 0);
+ ATF_REQUIRE(dstbuf[6] == 0xcccc);
+ ATF_REQUIRE(src == NULL);
+
+ /* Not enough space in destination buffer. */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "hello");
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbsrtowcs(dstbuf, (const char **)&src, 4, &s) == 4);
+ ATF_REQUIRE(wmemcmp(dstbuf, L"hell", 4) == 0);
+ ATF_REQUIRE(dstbuf[5] == 0xcccc);
+ ATF_REQUIRE(src == srcbuf + 4);
+
+ /* Null terminated string, internal dest. buffer */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "hello");
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(mbsrtowcs(NULL, (const char **)&src, 0, &s) == 5);
+
+ /* Null terminated string, internal state. */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "hello");
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ src = srcbuf;
+ ATF_REQUIRE(mbsrtowcs(dstbuf, (const char **)&src, sizeof(dstbuf) /
+ sizeof(*dstbuf), NULL) == 5);
+ ATF_REQUIRE(wcscmp(dstbuf, L"hello") == 0);
+ ATF_REQUIRE(dstbuf[6] == 0xcccc);
+ ATF_REQUIRE(src == NULL);
+
+ /* Null terminated string, internal state, internal dest. buffer. */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "hello");
+ src = srcbuf;
+ ATF_REQUIRE(mbsrtowcs(NULL, (const char **)&src, 0, NULL) == 5);
+
+ /* Empty source buffer. */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ srcbuf[0] = '\0';
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ ATF_REQUIRE(mbsrtowcs(dstbuf, (const char **)&src, 1, &s) == 0);
+ ATF_REQUIRE(dstbuf[0] == 0);
+ ATF_REQUIRE(dstbuf[1] == 0xcccc);
+ ATF_REQUIRE(src == NULL);
+
+ /* Zero length destination buffer. */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "hello");
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ ATF_REQUIRE(mbsrtowcs(dstbuf, (const char **)&src, 0, &s) == 0);
+ ATF_REQUIRE(dstbuf[0] == 0xcccc);
+ ATF_REQUIRE(src == srcbuf);
+
+ /*
+ * Japanese (EUC) locale.
+ */
+
+ ATF_REQUIRE(strcmp(setlocale(LC_CTYPE, "ja_JP.eucJP"), "ja_JP.eucJP") == 0);
+ ATF_REQUIRE(MB_CUR_MAX > 1);
+
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "\xA3\xC1 B \xA3\xC3");
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ ATF_REQUIRE(mbsrtowcs(dstbuf, (const char **)&src, sizeof(dstbuf) /
+ sizeof(*dstbuf), &s) == 5);
+ ATF_REQUIRE(dstbuf[0] == 0xA3C1 && dstbuf[1] == 0x20 && dstbuf[2] == 0x42 &&
+ dstbuf[3] == 0x20 && dstbuf[4] == 0xA3C3 && dstbuf[5] == 0);
+ ATF_REQUIRE(src == NULL);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mbsrtowcs_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/locale/mbstowcs_test.c b/lib/libc/tests/locale/mbstowcs_test.c
new file mode 100644
index 000000000000..404adfd7b19a
--- /dev/null
+++ b/lib/libc/tests/locale/mbstowcs_test.c
@@ -0,0 +1,108 @@
+/*-
+ * Copyright (c) 2002 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test program for mbstowcs(), as specified by IEEE Std. 1003.1-2001 and
+ * ISO/IEC 9899:1999.
+ *
+ * The function is tested with both the "C" ("POSIX") LC_CTYPE setting and
+ * "ja_JP.eucJP". Other encodings are not tested.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(mbstowcs_test);
+ATF_TC_BODY(mbstowcs_test, tc)
+{
+ char srcbuf[128];
+ wchar_t dstbuf[128];
+
+ /* C/POSIX locale. */
+
+ /* Simple null terminated string. */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "hello");
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ ATF_REQUIRE(mbstowcs(dstbuf, srcbuf, sizeof(dstbuf) / sizeof(*dstbuf)) == 5);
+ ATF_REQUIRE(wcscmp(dstbuf, L"hello") == 0);
+ ATF_REQUIRE(dstbuf[6] == 0xcccc);
+
+ /* Not enough space in destination buffer. */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "hello");
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ ATF_REQUIRE(mbstowcs(dstbuf, srcbuf, 4) == 4);
+ ATF_REQUIRE(wmemcmp(dstbuf, L"hell", 4) == 0);
+ ATF_REQUIRE(dstbuf[5] == 0xcccc);
+
+ /* Null terminated string, internal dest. buffer (XSI extension) */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "hello");
+ ATF_REQUIRE(mbstowcs(NULL, srcbuf, 0) == 5);
+
+ /* Empty source buffer. */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ srcbuf[0] = '\0';
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ ATF_REQUIRE(mbstowcs(dstbuf, srcbuf, 1) == 0);
+ ATF_REQUIRE(dstbuf[0] == 0);
+ ATF_REQUIRE(dstbuf[1] == 0xcccc);
+
+ /* Zero length destination buffer. */
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "hello");
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ ATF_REQUIRE(mbstowcs(dstbuf, srcbuf, 0) == 0);
+ ATF_REQUIRE(dstbuf[0] == 0xcccc);
+
+ /* Japanese (EUC) locale. */
+
+ ATF_REQUIRE(strcmp(setlocale(LC_CTYPE, "ja_JP.eucJP"), "ja_JP.eucJP") == 0);
+ ATF_REQUIRE(MB_CUR_MAX > 1);
+
+ memset(srcbuf, 0xcc, sizeof(srcbuf));
+ strcpy(srcbuf, "\xA3\xC1 B \xA3\xC3");
+ wmemset(dstbuf, 0xcccc, sizeof(dstbuf) / sizeof(*dstbuf));
+ ATF_REQUIRE(mbstowcs(dstbuf, srcbuf, sizeof(dstbuf) / sizeof(*dstbuf)) == 5);
+ ATF_REQUIRE(dstbuf[0] == 0xA3C1 && dstbuf[1] == 0x20 && dstbuf[2] == 0x42 &&
+ dstbuf[3] == 0x20 && dstbuf[4] == 0xA3C3 && dstbuf[5] == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mbstowcs_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/locale/mbtowc_test.c b/lib/libc/tests/locale/mbtowc_test.c
new file mode 100644
index 000000000000..e5ff276b4610
--- /dev/null
+++ b/lib/libc/tests/locale/mbtowc_test.c
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 2002-2004 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test program for mbtowc(), as specified by IEEE Std. 1003.1-2001 and
+ * ISO/IEC 9899:1990.
+ *
+ * The function is tested with both the "C" ("POSIX") LC_CTYPE setting and
+ * "ja_JP.eucJP". Other encodings are not tested.
+ */
+
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(mbtowc_test);
+ATF_TC_BODY(mbtowc_test, tc)
+{
+ char buf[MB_LEN_MAX + 1];
+ wchar_t wc;
+
+ /* C/POSIX locale. */
+
+ ATF_REQUIRE(MB_CUR_MAX == 1);
+
+ /* No shift states in C locale. */
+ ATF_REQUIRE(mbtowc(NULL, NULL, 0) == 0);
+
+ /* Null wide character. */
+ wc = 0xcccc;
+ memset(buf, 0, sizeof(buf));
+ ATF_REQUIRE(mbtowc(&wc, buf, 1) == 0);
+ ATF_REQUIRE(wc == 0);
+
+ /* Latin letter A. */
+ buf[0] = 'A';
+ ATF_REQUIRE(mbtowc(&wc, buf, 1) == 1);
+ ATF_REQUIRE(wc == L'A');
+
+ /* Incomplete character sequence. */
+ wc = L'z';
+ buf[0] = '\0';
+ ATF_REQUIRE(mbtowc(&wc, buf, 0) == -1);
+ ATF_REQUIRE(wc == L'z');
+ ATF_REQUIRE(mbtowc(NULL, NULL, 0) == 0);
+
+ /* Japanese (EUC) locale. */
+
+ ATF_REQUIRE(strcmp(setlocale(LC_CTYPE, "ja_JP.eucJP"), "ja_JP.eucJP") == 0);
+ ATF_REQUIRE(MB_CUR_MAX > 1);
+
+ /* Null wide character */
+ memset(buf, 0xcc, sizeof(buf));
+ buf[0] = 0;
+ wc = 0xcccc;
+ ATF_REQUIRE(mbtowc(&wc, buf, 1) == 0);
+ ATF_REQUIRE(wc == 0);
+
+ /* Latin letter A. */
+ buf[0] = 'A';
+ ATF_REQUIRE(mbtowc(&wc, buf, 1) == 1);
+ ATF_REQUIRE(wc == L'A');
+
+ /* Incomplete character sequence (zero length). */
+ wc = L'z';
+ buf[0] = '\0';
+ ATF_REQUIRE(mbtowc(&wc, buf, 0) == -1);
+ ATF_REQUIRE(wc == L'z');
+ ATF_REQUIRE(mbtowc(NULL, NULL, 0) == 0);
+
+ /* Incomplete character sequence (truncated double-byte). */
+ memset(buf, 0xcc, sizeof(buf));
+ buf[0] = 0xa3;
+ buf[1] = 0x00;
+ wc = L'z';
+ ATF_REQUIRE(mbtowc(&wc, buf, 1) == -1);
+ ATF_REQUIRE(wc == L'z');
+ ATF_REQUIRE(mbtowc(NULL, NULL, 0) == 0);
+
+ /* Same as above, but complete. */
+ buf[1] = 0xc1;
+ ATF_REQUIRE(mbtowc(&wc, buf, 2) == 2);
+ ATF_REQUIRE(wc == 0xa3c1);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mbtowc_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/locale/newlocale_test.c b/lib/libc/tests/locale/newlocale_test.c
new file mode 100644
index 000000000000..cb0ebb323a42
--- /dev/null
+++ b/lib/libc/tests/locale/newlocale_test.c
@@ -0,0 +1,111 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2023 Yuri Pankov <yuripv@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+
+#include <locale.h>
+
+#include <atf-c.h>
+
+struct {
+ int lpmask;
+ const char *lpname;
+} lparts[] = {
+ { LC_COLLATE_MASK, "LC_COLLATE" },
+ { LC_CTYPE_MASK, "LC_CTYPE" },
+ { LC_MONETARY_MASK, "LC_MONETARY" },
+ { LC_NUMERIC_MASK, "LC_NUMERIC" },
+ { LC_TIME_MASK, "LC_TIME" },
+ { LC_MESSAGES_MASK, "LC_MESSAGES" },
+};
+
+static void
+check_lparts(const char *expected)
+{
+ int i;
+
+ for (i = 0; i < nitems(lparts); i++) {
+ const char *actual;
+
+ actual = querylocale(lparts[i].lpmask, uselocale(NULL));
+ ATF_CHECK_STREQ_MSG(expected, actual, "wrong value for %s",
+ lparts[i].lpname);
+ }
+}
+
+static void
+do_locale_switch(const char *loc1, const char *loc2)
+{
+ locale_t l1, l2;
+
+ /* Create and use the first locale */
+ l1 = newlocale(LC_ALL_MASK, loc1, NULL);
+ ATF_REQUIRE(l1 != NULL);
+ ATF_REQUIRE(uselocale(l1) != NULL);
+ check_lparts(loc1);
+ /*
+ * Create and use second locale, creation deliberately done only after
+ * the first locale check as newlocale() call would previously clobber
+ * the first locale contents.
+ */
+ l2 = newlocale(LC_ALL_MASK, loc2, NULL);
+ ATF_REQUIRE(l2 != NULL);
+ ATF_REQUIRE(uselocale(l2) != NULL);
+ check_lparts(loc2);
+ /* Switch back to first locale */
+ ATF_REQUIRE(uselocale(l1) != NULL);
+ check_lparts(loc1);
+
+ freelocale(l1);
+ freelocale(l2);
+}
+
+/*
+ * PR 255646, 269375: Check that newlocale()/uselocale() used to switch between
+ * C, POSIX, and C.UTF-8 locales (and only these) do not stomp on other locale
+ * contents (collate part specifically).
+ * The issue is cosmetic only as all three have empty collate parts, but we need
+ * to correctly report the one in use in any case.
+ */
+
+ATF_TC_WITHOUT_HEAD(newlocale_c_posix_cu8_test);
+ATF_TC_BODY(newlocale_c_posix_cu8_test, tc)
+{
+ do_locale_switch("C", "POSIX");
+ do_locale_switch("C", "C.UTF-8");
+ do_locale_switch("POSIX", "C");
+ do_locale_switch("POSIX", "C.UTF-8");
+ do_locale_switch("C.UTF-8", "C");
+ do_locale_switch("C.UTF-8", "POSIX");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, newlocale_c_posix_cu8_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/locale/towctrans_test.c b/lib/libc/tests/locale/towctrans_test.c
new file mode 100644
index 000000000000..f34eb554f283
--- /dev/null
+++ b/lib/libc/tests/locale/towctrans_test.c
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 2003 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test program for wctrans() and towctrans() as specified by
+ * IEEE Std. 1003.1-2001 and ISO/IEC 9899:1999.
+ *
+ * The functions are tested in the "C" and "ja_JP.eucJP" locales.
+ */
+
+#include <locale.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(towctrans_test);
+ATF_TC_BODY(towctrans_test, tc)
+{
+ wctype_t t;
+ int i, j;
+ struct {
+ const char *name;
+ wint_t (*func)(wint_t);
+ } tran[] = {
+ { "tolower", towlower },
+ { "toupper", towupper },
+ };
+
+ /* C/POSIX locale. */
+ for (i = 0; i < sizeof(tran) / sizeof(*tran); i++) {
+ t = wctrans(tran[i].name);
+ ATF_REQUIRE(t != 0);
+ for (j = 0; j < 256; j++)
+ ATF_REQUIRE(tran[i].func(j) == towctrans(j, t));
+ }
+ t = wctrans("elephant");
+ ATF_REQUIRE(t == 0);
+ for (i = 0; i < 256; i++)
+ ATF_REQUIRE(towctrans(i, t) == i);
+
+ /* Japanese (EUC) locale. */
+ ATF_REQUIRE(strcmp(setlocale(LC_CTYPE, "ja_JP.eucJP"), "ja_JP.eucJP") == 0);
+ for (i = 0; i < sizeof(tran) / sizeof(*tran); i++) {
+ t = wctrans(tran[i].name);
+ ATF_REQUIRE(t != 0);
+ for (j = 0; j < 65536; j++)
+ ATF_REQUIRE(tran[i].func(j) == towctrans(j, t));
+ }
+ t = wctrans("elephant");
+ ATF_REQUIRE(t == 0);
+ for (i = 0; i < 65536; i++)
+ ATF_REQUIRE(towctrans(i, t) == i);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, towctrans_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/locale/wcrtomb_test.c b/lib/libc/tests/locale/wcrtomb_test.c
new file mode 100644
index 000000000000..2bc7cbc2365f
--- /dev/null
+++ b/lib/libc/tests/locale/wcrtomb_test.c
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (c) 2002 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test program for wcrtomb(), as specified by IEEE Std. 1003.1-2001 and
+ * ISO/IEC 9899:1999.
+ *
+ * The function is tested with both the "C" ("POSIX") LC_CTYPE setting and
+ * "ja_JP.eucJP". Other encodings are not tested.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(wcrtomb_test);
+ATF_TC_BODY(wcrtomb_test, tc)
+{
+ mbstate_t s;
+ size_t len;
+ char buf[MB_LEN_MAX + 1];
+
+ /* C/POSIX locale. */
+
+ ATF_REQUIRE(MB_CUR_MAX == 1);
+
+ /*
+ * If the buffer argument is NULL, wc is implicitly L'\0',
+ * wcrtomb() resets its internal state.
+ */
+ ATF_REQUIRE(wcrtomb(NULL, L'\0', NULL) == 1);
+ ATF_REQUIRE(wcrtomb(NULL, UCHAR_MAX + 1, NULL) == 1);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ len = wcrtomb(buf, L'\0', &s);
+ ATF_REQUIRE(len == 1);
+ ATF_REQUIRE((unsigned char)buf[0] == 0 && (unsigned char)buf[1] == 0xcc);
+
+ /* Latin letter A, internal state. */
+ ATF_REQUIRE(wcrtomb(NULL, L'\0', NULL) == 1);
+ ATF_REQUIRE(wcrtomb(NULL, L'A', NULL) == 1);
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ len = wcrtomb(buf, L'A', &s);
+ ATF_REQUIRE(len == 1);
+ ATF_REQUIRE((unsigned char)buf[0] == 'A' && (unsigned char)buf[1] == 0xcc);
+
+ /* Invalid code. */
+ ATF_REQUIRE(wcrtomb(buf, UCHAR_MAX + 1, NULL) == (size_t)-1);
+ ATF_REQUIRE(errno == EILSEQ);
+
+ /*
+ * Japanese (EUC) locale.
+ */
+
+ ATF_REQUIRE(strcmp(setlocale(LC_CTYPE, "ja_JP.eucJP"), "ja_JP.eucJP") == 0);
+ ATF_REQUIRE(MB_CUR_MAX == 3);
+
+ /*
+ * If the buffer argument is NULL, wc is implicitly L'\0',
+ * wcrtomb() resets its internal state.
+ */
+ ATF_REQUIRE(wcrtomb(NULL, L'\0', NULL) == 1);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ len = wcrtomb(buf, L'\0', &s);
+ ATF_REQUIRE(len == 1);
+ ATF_REQUIRE((unsigned char)buf[0] == 0 && (unsigned char)buf[1] == 0xcc);
+
+ /* Latin letter A, internal state. */
+ ATF_REQUIRE(wcrtomb(NULL, L'\0', NULL) == 1);
+ ATF_REQUIRE(wcrtomb(NULL, L'A', NULL) == 1);
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ len = wcrtomb(buf, L'A', &s);
+ ATF_REQUIRE(len == 1);
+ ATF_REQUIRE((unsigned char)buf[0] == 'A' && (unsigned char)buf[1] == 0xcc);
+
+ /* Full width letter A. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ len = wcrtomb(buf, 0xa3c1, &s);
+ ATF_REQUIRE(len == 2);
+ ATF_REQUIRE((unsigned char)buf[0] == 0xa3 &&
+ (unsigned char)buf[1] == 0xc1 &&
+ (unsigned char)buf[2] == 0xcc);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, wcrtomb_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/locale/wcsnrtombs_test.c b/lib/libc/tests/locale/wcsnrtombs_test.c
new file mode 100644
index 000000000000..8764c1567066
--- /dev/null
+++ b/lib/libc/tests/locale/wcsnrtombs_test.c
@@ -0,0 +1,192 @@
+/*-
+ * Copyright (c) 2002-2004 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test program for wcsnrtombs().
+ *
+ * The function is tested with both the "C" ("POSIX") LC_CTYPE setting and
+ * "ja_JP.eucJP". Other encodings are not tested.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(wcsnrtombs_test);
+ATF_TC_BODY(wcsnrtombs_test, tc)
+{
+ wchar_t srcbuf[128];
+ char dstbuf[128];
+ wchar_t *src;
+ mbstate_t s;
+
+ /* C/POSIX locale. */
+
+ /* Simple null terminated string. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(wcsnrtombs(dstbuf, (const wchar_t **)&src, 6, sizeof(dstbuf),
+ &s) == 5);
+ ATF_REQUIRE(strcmp(dstbuf, "hello") == 0);
+ ATF_REQUIRE((unsigned char)dstbuf[6] == 0xcc);
+ ATF_REQUIRE(src == NULL);
+
+ /* Simple null terminated string, stopping early. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(wcsnrtombs(dstbuf, (const wchar_t **)&src, 4, sizeof(dstbuf),
+ &s) == 4);
+ ATF_REQUIRE(memcmp(dstbuf, "hell", 4) == 0);
+ ATF_REQUIRE((unsigned char)dstbuf[5] == 0xcc);
+ ATF_REQUIRE(src == srcbuf + 4);
+
+ /* Not enough space in destination buffer. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(wcsnrtombs(dstbuf, (const wchar_t **)&src, 6, 4,
+ &s) == 4);
+ ATF_REQUIRE(memcmp(dstbuf, "hell", 4) == 0);
+ ATF_REQUIRE((unsigned char)dstbuf[5] == 0xcc);
+ ATF_REQUIRE(src == srcbuf + 4);
+
+ /* Null terminated string, internal dest. buffer */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(wcsnrtombs(NULL, (const wchar_t **)&src, 6, sizeof(dstbuf),
+ &s) == 5);
+
+ /* Null terminated string, internal dest. buffer, stopping early. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(wcsnrtombs(NULL, (const wchar_t **)&src, 4, sizeof(dstbuf),
+ &s) == 4);
+
+ /* Null terminated string, internal state. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ src = srcbuf;
+ ATF_REQUIRE(wcsnrtombs(dstbuf, (const wchar_t **)&src, 6, sizeof(dstbuf),
+ NULL) == 5);
+ ATF_REQUIRE(strcmp(dstbuf, "hello") == 0);
+ ATF_REQUIRE((unsigned char)dstbuf[6] == 0xcc);
+ ATF_REQUIRE(src == NULL);
+
+ /* Null terminated string, internal state, internal dest. buffer. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ src = srcbuf;
+ ATF_REQUIRE(wcsnrtombs(NULL, (const wchar_t **)&src, 6, 0, NULL) == 5);
+
+ /* Empty source buffer. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ srcbuf[0] = L'\0';
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(wcsnrtombs(dstbuf, (const wchar_t **)&src, 1, sizeof(dstbuf),
+ &s) == 0);
+ ATF_REQUIRE(dstbuf[0] == L'\0');
+
+ /* Zero length destination buffer. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(wcsnrtombs(dstbuf, (const wchar_t **)&src, 6, 0, &s) == 0);
+ ATF_REQUIRE((unsigned char)dstbuf[0] == 0xcc);
+
+ /* Zero length source buffer. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(wcsnrtombs(dstbuf, (const wchar_t **)&src, 0, sizeof(dstbuf),
+ &s) == 0);
+ ATF_REQUIRE((unsigned char)dstbuf[0] == 0xcc);
+ ATF_REQUIRE(src == srcbuf);
+
+ /*
+ * Japanese (EUC) locale.
+ */
+
+ ATF_REQUIRE(strcmp(setlocale(LC_CTYPE, "ja_JP.eucJP"), "ja_JP.eucJP") == 0);
+ ATF_REQUIRE(MB_CUR_MAX > 1);
+
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ srcbuf[0] = 0xA3C1;
+ srcbuf[1] = 0x0020;
+ srcbuf[2] = 0x0042;
+ srcbuf[3] = 0x0020;
+ srcbuf[4] = 0xA3C3;
+ srcbuf[5] = 0x0000;
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(wcsnrtombs(dstbuf, (const wchar_t **)&src, 6, sizeof(dstbuf),
+ &s) == 7);
+ ATF_REQUIRE(strcmp(dstbuf, "\xA3\xC1 B \xA3\xC3") == 0);
+ ATF_REQUIRE((unsigned char)dstbuf[8] == 0xcc);
+ ATF_REQUIRE(src == NULL);
+
+ /* Stopping early. */
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(wcsnrtombs(dstbuf, (const wchar_t **)&src, 6, 6,
+ &s) == 5);
+ ATF_REQUIRE(memcmp(dstbuf, "\xA3\xC1 B ", 5) == 0);
+ ATF_REQUIRE((unsigned char)dstbuf[5] == 0xcc);
+ ATF_REQUIRE(src == srcbuf + 4);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, wcsnrtombs_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/locale/wcsrtombs_test.c b/lib/libc/tests/locale/wcsrtombs_test.c
new file mode 100644
index 000000000000..dcb111dfd87c
--- /dev/null
+++ b/lib/libc/tests/locale/wcsrtombs_test.c
@@ -0,0 +1,153 @@
+/*-
+ * Copyright (c) 2002 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test program for wcsrtombs(), as specified by IEEE Std. 1003.1-2001 and
+ * ISO/IEC 9899:1999.
+ *
+ * The function is tested with both the "C" ("POSIX") LC_CTYPE setting and
+ * "ja_JP.eucJP". Other encodings are not tested.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(wcsrtombs_test);
+ATF_TC_BODY(wcsrtombs_test, tc)
+{
+ wchar_t srcbuf[128];
+ char dstbuf[128];
+ wchar_t *src;
+ mbstate_t s;
+
+ /* C/POSIX locale. */
+
+ /* Simple null terminated string. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(wcsrtombs(dstbuf, (const wchar_t **)&src, sizeof(dstbuf),
+ &s) == 5);
+ ATF_REQUIRE(strcmp(dstbuf, "hello") == 0);
+ ATF_REQUIRE((unsigned char)dstbuf[6] == 0xcc);
+ ATF_REQUIRE(src == NULL);
+
+ /* Not enough space in destination buffer. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(wcsrtombs(dstbuf, (const wchar_t **)&src, 4,
+ &s) == 4);
+ ATF_REQUIRE(memcmp(dstbuf, "hell", 4) == 0);
+ ATF_REQUIRE((unsigned char)dstbuf[5] == 0xcc);
+ ATF_REQUIRE(src == srcbuf + 4);
+
+ /* Null terminated string, internal dest. buffer */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(wcsrtombs(NULL, (const wchar_t **)&src, sizeof(dstbuf),
+ &s) == 5);
+
+ /* Null terminated string, internal state. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ src = srcbuf;
+ ATF_REQUIRE(wcsrtombs(dstbuf, (const wchar_t **)&src, sizeof(dstbuf),
+ NULL) == 5);
+ ATF_REQUIRE(strcmp(dstbuf, "hello") == 0);
+ ATF_REQUIRE((unsigned char)dstbuf[6] == 0xcc);
+ ATF_REQUIRE(src == NULL);
+
+ /* Null terminated string, internal state, internal dest. buffer. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ src = srcbuf;
+ ATF_REQUIRE(wcsrtombs(NULL, (const wchar_t **)&src, 0, NULL) == 5);
+
+ /* Empty source buffer. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ srcbuf[0] = L'\0';
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(wcsrtombs(dstbuf, (const wchar_t **)&src, sizeof(dstbuf),
+ &s) == 0);
+ ATF_REQUIRE(dstbuf[0] == L'\0');
+
+ /* Zero length destination buffer. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(wcsrtombs(dstbuf, (const wchar_t **)&src, 0, &s) == 0);
+ ATF_REQUIRE((unsigned char)dstbuf[0] == 0xcc);
+
+ /*
+ * Japanese (EUC) locale.
+ */
+
+ ATF_REQUIRE(strcmp(setlocale(LC_CTYPE, "ja_JP.eucJP"), "ja_JP.eucJP") == 0);
+ ATF_REQUIRE(MB_CUR_MAX > 1);
+
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ srcbuf[0] = 0xA3C1;
+ srcbuf[1] = 0x0020;
+ srcbuf[2] = 0x0042;
+ srcbuf[3] = 0x0020;
+ srcbuf[4] = 0xA3C3;
+ srcbuf[5] = 0x0000;
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ src = srcbuf;
+ memset(&s, 0, sizeof(s));
+ ATF_REQUIRE(wcsrtombs(dstbuf, (const wchar_t **)&src, sizeof(dstbuf),
+ &s) == 7);
+ ATF_REQUIRE(strcmp(dstbuf, "\xA3\xC1 B \xA3\xC3") == 0);
+ ATF_REQUIRE((unsigned char)dstbuf[8] == 0xcc);
+ ATF_REQUIRE(src == NULL);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, wcsrtombs_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/locale/wcstombs_test.c b/lib/libc/tests/locale/wcstombs_test.c
new file mode 100644
index 000000000000..2506be2806f6
--- /dev/null
+++ b/lib/libc/tests/locale/wcstombs_test.c
@@ -0,0 +1,127 @@
+/*-
+ * Copyright (c) 2002 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test program for wcstombs(), as specified by IEEE Std. 1003.1-2001 and
+ * ISO/IEC 9899:1999.
+ *
+ * The function is tested with both the "C" ("POSIX") LC_CTYPE setting and
+ * "ja_JP.eucJP". Other encodings are not tested.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(wcstombs_test);
+ATF_TC_BODY(wcstombs_test, tc)
+{
+ wchar_t srcbuf[128];
+ char dstbuf[128];
+
+ /* C/POSIX locale. */
+
+ /* Simple null terminated string. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ ATF_REQUIRE(wcstombs(dstbuf, srcbuf, sizeof(dstbuf)) == 5);
+ ATF_REQUIRE(strcmp(dstbuf, "hello") == 0);
+ ATF_REQUIRE((unsigned char)dstbuf[6] == 0xcc);
+
+ /* Not enough space in destination buffer. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ ATF_REQUIRE(wcstombs(dstbuf, srcbuf, 4) == 4);
+ ATF_REQUIRE(memcmp(dstbuf, "hell", 4) == 0);
+ ATF_REQUIRE((unsigned char)dstbuf[5] == 0xcc);
+
+ /* Null terminated string, internal dest. buffer */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ ATF_REQUIRE(wcstombs(NULL, srcbuf, sizeof(dstbuf)) == 5);
+
+ /* Null terminated string, internal state. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ ATF_REQUIRE(wcstombs(dstbuf, srcbuf, sizeof(dstbuf)) == 5);
+ ATF_REQUIRE(strcmp(dstbuf, "hello") == 0);
+ ATF_REQUIRE((unsigned char)dstbuf[6] == 0xcc);
+
+ /* Null terminated string, internal state, internal dest. buffer. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ ATF_REQUIRE(wcstombs(NULL, srcbuf, 0) == 5);
+
+ /* Empty source buffer. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ srcbuf[0] = L'\0';
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ ATF_REQUIRE(wcstombs(dstbuf, srcbuf, sizeof(dstbuf)) == 0);
+ ATF_REQUIRE(dstbuf[0] == L'\0');
+
+ /* Zero length destination buffer. */
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ wcscpy(srcbuf, L"hello");
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ ATF_REQUIRE(wcstombs(dstbuf, srcbuf, 0) == 0);
+ ATF_REQUIRE((unsigned char)dstbuf[0] == 0xcc);
+
+ /*
+ * Japanese (EUC) locale.
+ */
+
+ ATF_REQUIRE(strcmp(setlocale(LC_CTYPE, "ja_JP.eucJP"), "ja_JP.eucJP") == 0);
+ ATF_REQUIRE(MB_CUR_MAX > 1);
+
+ wmemset(srcbuf, 0xcc, sizeof(srcbuf) / sizeof(*srcbuf));
+ srcbuf[0] = 0xA3C1;
+ srcbuf[1] = 0x0020;
+ srcbuf[2] = 0x0042;
+ srcbuf[3] = 0x0020;
+ srcbuf[4] = 0xA3C3;
+ srcbuf[5] = 0x0000;
+ memset(dstbuf, 0xcc, sizeof(dstbuf));
+ ATF_REQUIRE(wcstombs(dstbuf, srcbuf, sizeof(dstbuf)) == 7);
+ ATF_REQUIRE(strcmp(dstbuf, "\xA3\xC1 B \xA3\xC3") == 0);
+ ATF_REQUIRE((unsigned char)dstbuf[8] == 0xcc);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, wcstombs_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/locale/wctomb_test.c b/lib/libc/tests/locale/wctomb_test.c
new file mode 100644
index 000000000000..ef2a6dcbe1e3
--- /dev/null
+++ b/lib/libc/tests/locale/wctomb_test.c
@@ -0,0 +1,123 @@
+/*-
+ * Copyright (c) 2002-2004 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test program for wctomb(), as specified by IEEE Std. 1003.1-2001 and
+ * ISO/IEC 9899:1999.
+ *
+ * The function is tested with both the "C" ("POSIX") LC_CTYPE setting and
+ * "ja_JP.eucJP". Other encodings are not tested.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(euccs1_test);
+ATF_TC_BODY(euccs1_test, tc)
+{
+ wchar_t wc = 0x8e000000;
+ char buf[MB_LEN_MAX];
+
+ ATF_REQUIRE(strcmp(setlocale(LC_CTYPE, "zh_CN.eucCN"),
+ "zh_CN.eucCN") == 0);
+
+ ATF_REQUIRE(wctomb(&buf[0], wc) == 4);
+}
+
+ATF_TC_WITHOUT_HEAD(wctomb_test);
+ATF_TC_BODY(wctomb_test, tc)
+{
+ size_t len;
+ char buf[MB_LEN_MAX + 1];
+
+ /* C/POSIX locale. */
+
+ ATF_REQUIRE(MB_CUR_MAX == 1);
+
+ /* No shift states in C locale. */
+ ATF_REQUIRE(wctomb(NULL, L'\0') == 0);
+
+ /* Null wide character. */
+ memset(buf, 0xcc, sizeof(buf));
+ len = wctomb(buf, L'\0');
+ ATF_REQUIRE(len == 1);
+ ATF_REQUIRE((unsigned char)buf[0] == 0 && (unsigned char)buf[1] == 0xcc);
+
+ /* Latin letter A. */
+ memset(buf, 0xcc, sizeof(buf));
+ len = wctomb(buf, L'A');
+ ATF_REQUIRE(len == 1);
+ ATF_REQUIRE((unsigned char)buf[0] == 'A' && (unsigned char)buf[1] == 0xcc);
+
+ /* Invalid code. */
+ ATF_REQUIRE(wctomb(buf, UCHAR_MAX + 1) == -1);
+ ATF_REQUIRE(wctomb(NULL, 0) == 0);
+
+ /*
+ * Japanese (EUC) locale.
+ */
+
+ ATF_REQUIRE(strcmp(setlocale(LC_CTYPE, "ja_JP.eucJP"), "ja_JP.eucJP") == 0);
+ ATF_REQUIRE(MB_CUR_MAX == 3);
+
+ /* No shift states in EUC encoding. */
+ ATF_REQUIRE(wctomb(NULL, L'\0') == 0);
+
+ /* Null wide character. */
+ memset(buf, 0xcc, sizeof(buf));
+ len = wctomb(buf, L'\0');
+ ATF_REQUIRE(len == 1);
+ ATF_REQUIRE((unsigned char)buf[0] == 0 && (unsigned char)buf[1] == 0xcc);
+
+ /* Latin letter A. */
+ memset(buf, 0xcc, sizeof(buf));
+ len = wctomb(buf, L'A');
+ ATF_REQUIRE(len == 1);
+ ATF_REQUIRE((unsigned char)buf[0] == 'A' && (unsigned char)buf[1] == 0xcc);
+
+ /* Full width letter A. */
+ memset(buf, 0xcc, sizeof(buf));
+ len = wctomb(buf, 0xa3c1);
+ ATF_REQUIRE(len == 2);
+ ATF_REQUIRE((unsigned char)buf[0] == 0xa3 &&
+ (unsigned char)buf[1] == 0xc1 &&
+ (unsigned char)buf[2] == 0xcc);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, euccs1_test);
+ ATF_TP_ADD_TC(tp, wctomb_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/net/Makefile b/lib/libc/tests/net/Makefile
new file mode 100644
index 000000000000..24cff61e8d24
--- /dev/null
+++ b/lib/libc/tests/net/Makefile
@@ -0,0 +1,43 @@
+PACKAGE= tests
+
+ATF_TESTS_C+= ether_test
+ATF_TESTS_C+= eui64_aton_test
+ATF_TESTS_C+= eui64_ntoa_test
+ATF_TESTS_CXX+= link_addr_test
+
+CXXSTD.link_addr_test= c++20
+
+CFLAGS+= -I${.CURDIR}
+
+NETBSD_ATF_TESTS_C+= getprotoent_test
+NETBSD_ATF_TESTS_C+= ether_aton_test
+
+SRCS.ether_aton_test= aton_ether_subr.c t_ether_aton.c
+
+# TODO: hostent_test
+NETBSD_ATF_TESTS_SH= nsdispatch_test
+NETBSD_ATF_TESTS_SH+= protoent_test
+NETBSD_ATF_TESTS_SH+= servent_test
+
+BINDIR= ${TESTSDIR}
+
+PROGS= h_nsd_recurse
+PROGS+= h_protoent
+PROGS+= h_servent
+PROGS+= h_dns_server
+
+LIBADD.h_nsd_recurse+= pthread
+
+CLEANFILES+= aton_ether_subr.c
+aton_ether_subr.c: gen_ether_subr ${SRCTOP}/sys/net/if_ethersubr.c
+ ${__MAKE_SHELL} ${.ALLSRC} ${.TARGET}
+
+.include "../Makefile.netbsd-tests"
+
+TESTS_SUBDIRS= getaddrinfo
+${PACKAGE}FILES+= hosts
+${PACKAGE}FILES+= resolv.conf
+
+ATF_TESTS_SH_SED_servent_test= -e 's,services.cdb,services.db,g'
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/net/Makefile.depend b/lib/libc/tests/net/Makefile.depend
new file mode 100644
index 000000000000..daaf971bd513
--- /dev/null
+++ b/lib/libc/tests/net/Makefile.depend
@@ -0,0 +1,19 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libnetbsd \
+ lib/libthr \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/net/ether_test.c b/lib/libc/tests/net/ether_test.c
new file mode 100644
index 000000000000..fbeed15efaa7
--- /dev/null
+++ b/lib/libc/tests/net/ether_test.c
@@ -0,0 +1,190 @@
+/*-
+ * Copyright (c) 2007 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <net/ethernet.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+static const char *ether_line_string = "01:23:45:67:89:ab ether_line_hostname";
+static const char *ether_line_hostname = "ether_line_hostname";
+static const struct ether_addr ether_line_addr = {
+ { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }
+};
+
+ATF_TC_WITHOUT_HEAD(ether_line);
+ATF_TC_BODY(ether_line, tc)
+{
+ struct ether_addr e;
+ char hostname[256];
+
+ ATF_REQUIRE_MSG(ether_line(ether_line_string, &e, hostname) == 0,
+ "ether_line failed; errno=%d", errno);
+ ATF_REQUIRE_MSG(bcmp(&e, &ether_line_addr, ETHER_ADDR_LEN) == 0,
+ "bad address");
+ ATF_REQUIRE_MSG(strcmp(hostname, ether_line_hostname) == 0,
+ "bad hostname");
+}
+
+static const char *ether_line_bad_1_string = "x";
+
+ATF_TC_WITHOUT_HEAD(ether_line_bad_1);
+ATF_TC_BODY(ether_line_bad_1, tc)
+{
+ struct ether_addr e;
+ char hostname[256];
+
+ ATF_REQUIRE_MSG(ether_line(ether_line_bad_1_string, &e, hostname) != 0,
+ "ether_line succeeded unexpectedly");
+}
+
+static const char *ether_line_bad_2_string = "x x";
+
+ATF_TC_WITHOUT_HEAD(ether_line_bad_2);
+ATF_TC_BODY(ether_line_bad_2, tc)
+{
+ struct ether_addr e;
+ char hostname[256];
+
+ ATF_REQUIRE_MSG(ether_line(ether_line_bad_2_string, &e, hostname) != 0,
+ "ether_line succeeded unexpectedly");
+}
+
+static const char *ether_aton_string = "01:23:45:67:89:ab";
+static const struct ether_addr ether_aton_addr = {
+ { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }
+};
+
+ATF_TC_WITHOUT_HEAD(ether_aton_r);
+ATF_TC_BODY(ether_aton_r, tc)
+{
+ struct ether_addr e, *ep;
+
+ ep = ether_aton_r(ether_aton_string, &e);
+
+ ATF_REQUIRE_MSG(ep != NULL, "ether_aton_r failed; errno=%d", errno);
+ ATF_REQUIRE_MSG(ep == &e,
+ "ether_aton_r returned different pointers; %p != %p", ep, &e);
+}
+
+static const char *ether_aton_bad_string = "x";
+
+ATF_TC_WITHOUT_HEAD(ether_aton_r_bad);
+ATF_TC_BODY(ether_aton_r_bad, tc)
+{
+ struct ether_addr e, *ep;
+
+ ep = ether_aton_r(ether_aton_bad_string, &e);
+ ATF_REQUIRE_MSG(ep == NULL, "ether_aton_r succeeded unexpectedly");
+}
+
+ATF_TC_WITHOUT_HEAD(ether_aton);
+ATF_TC_BODY(ether_aton, tc)
+{
+ struct ether_addr *ep;
+
+ ep = ether_aton(ether_aton_string);
+ ATF_REQUIRE_MSG(ep != NULL, "ether_aton failed");
+ ATF_REQUIRE_MSG(bcmp(ep, &ether_aton_addr, ETHER_ADDR_LEN) == 0,
+ "bad address");
+}
+
+ATF_TC_WITHOUT_HEAD(ether_aton_bad);
+ATF_TC_BODY(ether_aton_bad, tc)
+{
+ struct ether_addr *ep;
+
+ ep = ether_aton(ether_aton_bad_string);
+ ATF_REQUIRE_MSG(ep == NULL, "ether_aton succeeded unexpectedly");
+}
+
+static const char *ether_ntoa_string = "01:23:45:67:89:ab";
+static const struct ether_addr ether_ntoa_addr = {
+ { 0x01, 0x23, 0x45, 0x67, 0x89, 0xab }
+};
+
+ATF_TC_WITHOUT_HEAD(ether_ntoa_r);
+ATF_TC_BODY(ether_ntoa_r, tc)
+{
+ char buf[256], *cp;
+
+ cp = ether_ntoa_r(&ether_ntoa_addr, buf);
+ ATF_REQUIRE_MSG(cp != NULL, "ether_ntoa_r failed");
+ ATF_REQUIRE_MSG(cp == buf,
+ "ether_ntoa_r returned a different pointer; %p != %p", cp, buf);
+ ATF_REQUIRE_MSG(strcmp(cp, ether_ntoa_string) == 0,
+ "strings did not match (`%s` != `%s`)", cp, ether_ntoa_string);
+}
+
+ATF_TC_WITHOUT_HEAD(ether_ntoa);
+ATF_TC_BODY(ether_ntoa, tc)
+{
+ char *cp;
+
+ cp = ether_ntoa(&ether_ntoa_addr);
+ ATF_REQUIRE_MSG(cp != NULL, "ether_ntoa failed");
+ ATF_REQUIRE_MSG(strcmp(cp, ether_ntoa_string) == 0,
+ "strings did not match (`%s` != `%s`)", cp, ether_ntoa_string);
+}
+
+#if 0
+ATF_TC_WITHOUT_HEAD(ether_ntohost);
+ATF_TC_BODY(ether_ntohost, tc)
+{
+
+}
+
+ATF_TC_WITHOUT_HEAD(ether_hostton);
+ATF_TC_BODY(ether_hostton, tc)
+{
+
+}
+#endif
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, ether_line);
+ ATF_TP_ADD_TC(tp, ether_line_bad_1);
+ ATF_TP_ADD_TC(tp, ether_line_bad_2);
+ ATF_TP_ADD_TC(tp, ether_aton_r);
+ ATF_TP_ADD_TC(tp, ether_aton_r_bad);
+ ATF_TP_ADD_TC(tp, ether_aton);
+ ATF_TP_ADD_TC(tp, ether_aton_bad);
+ ATF_TP_ADD_TC(tp, ether_ntoa_r);
+ ATF_TP_ADD_TC(tp, ether_ntoa);
+#if 0
+ ATF_TP_ADD_TC(tp, ether_ntohost);
+ ATF_TP_ADD_TC(tp, ether_hostton);
+#endif
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/net/eui64_aton_test.c b/lib/libc/tests/net/eui64_aton_test.c
new file mode 100644
index 000000000000..a4b09695fda9
--- /dev/null
+++ b/lib/libc/tests/net/eui64_aton_test.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2004 The Aerospace Corporation. 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 Aerospace Corporation may not be used to endorse or
+ * promote products derived from this software.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AEROSPACE CORPORATION "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AEROSPACE CORPORATION BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/eui64.h>
+#include <locale.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#include "test-eui64.h"
+
+static void
+test_str(const char *str, const struct eui64 *eui)
+{
+ struct eui64 e;
+ char buf[EUI64_SIZ];
+ int rc;
+
+ ATF_REQUIRE_MSG(eui64_aton(str, &e) == 0, "eui64_aton failed");
+ rc = memcmp(&e, eui, sizeof(e));
+ if (rc != 0) {
+ eui64_ntoa(&e, buf, sizeof(buf));
+ atf_tc_fail(
+ "eui64_aton(\"%s\", ..) failed; memcmp returned %d. "
+ "String obtained form eui64_ntoa was: `%s`",
+ str, rc, buf);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(id_ascii);
+ATF_TC_BODY(id_ascii, tc)
+{
+
+ test_str(test_eui64_id_ascii, &test_eui64_id);
+}
+
+ATF_TC_WITHOUT_HEAD(id_colon_ascii);
+ATF_TC_BODY(id_colon_ascii, tc)
+{
+
+ test_str(test_eui64_id_colon_ascii, &test_eui64_id);
+}
+
+ATF_TC_WITHOUT_HEAD(mac_ascii);
+ATF_TC_BODY(mac_ascii, tc)
+{
+
+ test_str(test_eui64_mac_ascii, &test_eui64_eui48);
+}
+
+ATF_TC_WITHOUT_HEAD(mac_colon_ascii);
+ATF_TC_BODY(mac_colon_ascii, tc)
+{
+
+ test_str(test_eui64_mac_colon_ascii, &test_eui64_eui48);
+}
+
+ATF_TC_WITHOUT_HEAD(hex_ascii);
+ATF_TC_BODY(hex_ascii, tc)
+{
+
+ test_str(test_eui64_hex_ascii, &test_eui64_id);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, id_ascii);
+ ATF_TP_ADD_TC(tp, id_colon_ascii);
+ ATF_TP_ADD_TC(tp, mac_ascii);
+ ATF_TP_ADD_TC(tp, mac_colon_ascii);
+ ATF_TP_ADD_TC(tp, hex_ascii);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/net/eui64_ntoa_test.c b/lib/libc/tests/net/eui64_ntoa_test.c
new file mode 100644
index 000000000000..1da80fc2ea02
--- /dev/null
+++ b/lib/libc/tests/net/eui64_ntoa_test.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2004 The Aerospace Corporation. 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 Aerospace Corporation may not be used to endorse or
+ * promote products derived from this software.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AEROSPACE CORPORATION "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AEROSPACE CORPORATION BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/eui64.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#include "test-eui64.h"
+
+static void
+test_str(const char *str, const struct eui64 *eui)
+{
+ char a[EUI64_SIZ];
+
+ ATF_REQUIRE_MSG(eui64_ntoa(&test_eui64_id, a, sizeof(a)) == 0,
+ "eui64_ntoa failed");
+ ATF_REQUIRE_MSG(strcmp(a, test_eui64_id_ascii) == 0,
+ "the strings mismatched: `%s` != `%s`", a, test_eui64_id_ascii);
+}
+
+ATF_TC_WITHOUT_HEAD(id_ascii);
+ATF_TC_BODY(id_ascii, tc)
+{
+
+ test_str(test_eui64_id_ascii, &test_eui64_id);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, id_ascii);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/net/getaddrinfo/Makefile b/lib/libc/tests/net/getaddrinfo/Makefile
new file mode 100644
index 000000000000..1299e91615b7
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/Makefile
@@ -0,0 +1,47 @@
+PACKAGE= tests
+
+.include <bsd.own.mk>
+
+BINDIR= ${TESTSDIR}
+
+ATF_TESTS_SH= getaddrinfo_test
+ATF_TESTS_C= getaddrinfo
+
+PROGS= h_gai
+
+FILESGROUPS+= ${PACKAGE}DATA_FILES
+${PACKAGE}DATA_FILESPACKAGE= tests
+
+${PACKAGE}DATA_FILESDIR= ${TESTSDIR}/data
+
+${PACKAGE}DATA_FILES+= data/basics_v4.exp
+${PACKAGE}DATA_FILES+= data/basics_v4_only.exp
+${PACKAGE}DATA_FILES+= data/basics_v4v6.exp
+${PACKAGE}DATA_FILES+= data/basics_v4v6_prefer_v4.exp
+${PACKAGE}DATA_FILES+= data/no_host_v4.exp
+${PACKAGE}DATA_FILES+= data/no_host_v4_only.exp
+${PACKAGE}DATA_FILES+= data/no_host_v4v6.exp
+${PACKAGE}DATA_FILES+= data/no_host_v4v6_prefer_v4.exp
+${PACKAGE}DATA_FILES+= data/no_serv_v4.exp
+${PACKAGE}DATA_FILES+= data/no_serv_v4_only.exp
+${PACKAGE}DATA_FILES+= data/no_serv_v4v6.exp
+${PACKAGE}DATA_FILES+= data/no_serv_v4v6_prefer_v4.exp
+${PACKAGE}DATA_FILES+= data/scoped.exp
+${PACKAGE}DATA_FILES+= data/scoped_v4_only.exp
+${PACKAGE}DATA_FILES+= data/scoped_v4v6_prefer_v4.exp
+${PACKAGE}DATA_FILES+= data/sock_raw_v4.exp
+${PACKAGE}DATA_FILES+= data/sock_raw_v4_only.exp
+${PACKAGE}DATA_FILES+= data/sock_raw_v4v6.exp
+${PACKAGE}DATA_FILES+= data/sock_raw_v4v6_prefer_v4.exp
+${PACKAGE}DATA_FILES+= data/spec_fam_v4.exp
+${PACKAGE}DATA_FILES+= data/spec_fam_v4_only.exp
+${PACKAGE}DATA_FILES+= data/spec_fam_v4v6.exp
+${PACKAGE}DATA_FILES+= data/spec_fam_v4v6_prefer_v4.exp
+${PACKAGE}DATA_FILES+= data/unsup_fam.exp
+${PACKAGE}DATA_FILES+= data/unsup_fam_v4_only.exp
+${PACKAGE}DATA_FILES+= data/unsup_fam_v4v6_prefer_v4.exp
+
+
+.include "../../Makefile.netbsd-tests"
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/net/getaddrinfo/data/basics_v4.exp b/lib/libc/tests/net/getaddrinfo/data/basics_v4.exp
new file mode 100644
index 000000000000..4f1ee3517211
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/basics_v4.exp
@@ -0,0 +1,42 @@
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv http
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv http
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv http
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv tftp
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv tftp
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv tftp
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv tftp
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv tftp
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv tftp
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv tftp
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv tftp
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv tftp
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv echo
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv echo
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv echo
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv echo
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv echo
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv echo
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv echo
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv echo
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv echo
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv echo
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv echo
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv echo
+
diff --git a/lib/libc/tests/net/getaddrinfo/data/basics_v4_only.exp b/lib/libc/tests/net/getaddrinfo/data/basics_v4_only.exp
new file mode 100644
index 000000000000..0a37d3212649
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/basics_v4_only.exp
@@ -0,0 +1,50 @@
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv http
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv http
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv http
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+ai4: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http
+ai5: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http
+ai6: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv tftp
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv tftp
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv tftp
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv tftp
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv tftp
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv tftp
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv tftp
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv tftp
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv tftp
+ai3: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv tftp
+ai4: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv tftp
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv echo
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv echo
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv echo
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv echo
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv echo
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv echo
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv echo
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv echo
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv echo
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv echo
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv echo
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv echo
+ai4: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv echo
+ai5: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv echo
+ai6: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv echo
+
diff --git a/lib/libc/tests/net/getaddrinfo/data/basics_v4v6.exp b/lib/libc/tests/net/getaddrinfo/data/basics_v4v6.exp
new file mode 100644
index 000000000000..b74758e93e2e
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/basics_v4v6.exp
@@ -0,0 +1,50 @@
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv http
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv http
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv http
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http
+ai4: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai5: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai6: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv tftp
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv tftp
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv tftp
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv tftp
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv tftp
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv tftp
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv tftp
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv tftp
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv tftp
+ai3: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv tftp
+ai4: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv tftp
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv echo
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv echo
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv echo
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv echo
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv echo
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv echo
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv echo
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv echo
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv echo
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv echo
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv echo
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv echo
+ai4: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv echo
+ai5: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv echo
+ai6: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv echo
+
diff --git a/lib/libc/tests/net/getaddrinfo/data/basics_v4v6_prefer_v4.exp b/lib/libc/tests/net/getaddrinfo/data/basics_v4v6_prefer_v4.exp
new file mode 100644
index 000000000000..0a37d3212649
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/basics_v4v6_prefer_v4.exp
@@ -0,0 +1,50 @@
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv http
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv http
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv http
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+ai4: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http
+ai5: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http
+ai6: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv tftp
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv tftp
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv tftp
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv tftp
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv tftp
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv tftp
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv tftp
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv tftp
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv tftp
+ai3: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv tftp
+ai4: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv tftp
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv echo
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv echo
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv echo
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv echo
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv echo
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv echo
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv echo
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv echo
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv echo
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv echo
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv echo
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv echo
+ai4: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv echo
+ai5: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv echo
+ai6: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv echo
+
diff --git a/lib/libc/tests/net/getaddrinfo/data/generate_testdata.sh b/lib/libc/tests/net/getaddrinfo/data/generate_testdata.sh
new file mode 100755
index 000000000000..f0425a3b0283
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/generate_testdata.sh
@@ -0,0 +1,45 @@
+#service ip6addrctl prefer_ipv4
+TEST=./h_gai
+family=v4_only
+
+( $TEST ::1 http
+ $TEST 127.0.0.1 http
+ $TEST localhost http
+ $TEST ::1 tftp
+ $TEST 127.0.0.1 tftp
+ $TEST localhost tftp
+ $TEST ::1 echo
+ $TEST 127.0.0.1 echo
+ $TEST localhost echo ) > basics_${family}.exp
+
+( $TEST -4 localhost http
+ $TEST -6 localhost http ) > spec_fam_${family}.exp
+
+( $TEST '' http
+ $TEST '' echo
+ $TEST '' tftp
+ $TEST '' 80
+ $TEST -P '' http
+ $TEST -P '' echo
+ $TEST -P '' tftp
+ $TEST -P '' 80
+ $TEST -S '' 80
+ $TEST -D '' 80 ) > no_host_${family}.exp
+
+( $TEST ::1 ''
+ $TEST 127.0.0.1 ''
+ $TEST localhost ''
+ $TEST '' '' ) > no_serv_${family}.exp
+
+( $TEST -R -p 0 localhost ''
+ $TEST -R -p 59 localhost ''
+ $TEST -R -p 59 localhost 80
+ $TEST -R -p 59 localhost www
+ $TEST -R -p 59 ::1 '' ) > sock_raw_${family}.exp
+
+( $TEST -f 99 localhost '' ) > unsup_fam_${family}.exp
+
+( $TEST fe80::1%lo0 http
+# IF=`ifconfig -a | grep -v '^ ' | sed -e 's/:.*//' | head -1 | awk '{print $1}'`
+# $TEST fe80::1%$IF http
+) > scoped_${family}.exp
diff --git a/lib/libc/tests/net/getaddrinfo/data/no_host_v4.exp b/lib/libc/tests/net/getaddrinfo/data/no_host_v4.exp
new file mode 100644
index 000000000000..7ed41cd9d88a
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/no_host_v4.exp
@@ -0,0 +1,44 @@
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv http
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv echo
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv echo
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv echo
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv echo
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv tftp
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv tftp
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv tftp
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv 80
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv http
+ai1: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv http
+ai2: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv http
+ai3: flags 0x3 family 2 socktype 5 protocol 132 addrlen 16 host 0.0.0.0 serv http
+
+arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv echo
+ai1: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv echo
+ai2: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv echo
+ai3: flags 0x3 family 2 socktype 5 protocol 132 addrlen 16 host 0.0.0.0 serv echo
+
+arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv tftp
+ai1: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv tftp
+ai2: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv tftp
+
+arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv 80
+ai1: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv http
+ai2: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv http
+ai3: flags 0x3 family 2 socktype 5 protocol 132 addrlen 16 host 0.0.0.0 serv http
+
+arg: flags 0x2 family 0 socktype 1 protocol 0 addrlen 0 host (empty) serv 80
+ai1: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x2 family 0 socktype 2 protocol 0 addrlen 0 host (empty) serv 80
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+
diff --git a/lib/libc/tests/net/getaddrinfo/data/no_host_v4_only.exp b/lib/libc/tests/net/getaddrinfo/data/no_host_v4_only.exp
new file mode 100644
index 000000000000..596799305117
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/no_host_v4_only.exp
@@ -0,0 +1,68 @@
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv http
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http
+ai4: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai5: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai6: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv echo
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv echo
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv echo
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv echo
+ai4: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv echo
+ai5: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv echo
+ai6: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv echo
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv tftp
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv tftp
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv tftp
+ai3: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv tftp
+ai4: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv tftp
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv 80
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http
+ai4: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai5: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai6: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv http
+ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv http
+ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv http
+ai3: flags 0x3 family 28 socktype 5 protocol 132 addrlen 28 host :: serv http
+ai4: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv http
+ai5: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv http
+ai6: flags 0x3 family 2 socktype 5 protocol 132 addrlen 16 host 0.0.0.0 serv http
+
+arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv echo
+ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv echo
+ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv echo
+ai3: flags 0x3 family 28 socktype 5 protocol 132 addrlen 28 host :: serv echo
+ai4: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv echo
+ai5: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv echo
+ai6: flags 0x3 family 2 socktype 5 protocol 132 addrlen 16 host 0.0.0.0 serv echo
+
+arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv tftp
+ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv tftp
+ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv tftp
+ai3: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv tftp
+ai4: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv tftp
+
+arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv 80
+ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv http
+ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv http
+ai3: flags 0x3 family 28 socktype 5 protocol 132 addrlen 28 host :: serv http
+ai4: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv http
+ai5: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv http
+ai6: flags 0x3 family 2 socktype 5 protocol 132 addrlen 16 host 0.0.0.0 serv http
+
+arg: flags 0x2 family 0 socktype 1 protocol 0 addrlen 0 host (empty) serv 80
+ai1: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x2 family 0 socktype 2 protocol 0 addrlen 0 host (empty) serv 80
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+
diff --git a/lib/libc/tests/net/getaddrinfo/data/no_host_v4v6.exp b/lib/libc/tests/net/getaddrinfo/data/no_host_v4v6.exp
new file mode 100644
index 000000000000..596799305117
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/no_host_v4v6.exp
@@ -0,0 +1,68 @@
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv http
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http
+ai4: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai5: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai6: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv echo
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv echo
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv echo
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv echo
+ai4: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv echo
+ai5: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv echo
+ai6: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv echo
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv tftp
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv tftp
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv tftp
+ai3: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv tftp
+ai4: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv tftp
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv 80
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http
+ai4: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai5: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai6: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv http
+ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv http
+ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv http
+ai3: flags 0x3 family 28 socktype 5 protocol 132 addrlen 28 host :: serv http
+ai4: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv http
+ai5: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv http
+ai6: flags 0x3 family 2 socktype 5 protocol 132 addrlen 16 host 0.0.0.0 serv http
+
+arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv echo
+ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv echo
+ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv echo
+ai3: flags 0x3 family 28 socktype 5 protocol 132 addrlen 28 host :: serv echo
+ai4: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv echo
+ai5: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv echo
+ai6: flags 0x3 family 2 socktype 5 protocol 132 addrlen 16 host 0.0.0.0 serv echo
+
+arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv tftp
+ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv tftp
+ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv tftp
+ai3: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv tftp
+ai4: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv tftp
+
+arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv 80
+ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv http
+ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv http
+ai3: flags 0x3 family 28 socktype 5 protocol 132 addrlen 28 host :: serv http
+ai4: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv http
+ai5: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv http
+ai6: flags 0x3 family 2 socktype 5 protocol 132 addrlen 16 host 0.0.0.0 serv http
+
+arg: flags 0x2 family 0 socktype 1 protocol 0 addrlen 0 host (empty) serv 80
+ai1: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x2 family 0 socktype 2 protocol 0 addrlen 0 host (empty) serv 80
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+
diff --git a/lib/libc/tests/net/getaddrinfo/data/no_host_v4v6_prefer_v4.exp b/lib/libc/tests/net/getaddrinfo/data/no_host_v4v6_prefer_v4.exp
new file mode 100644
index 000000000000..596799305117
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/no_host_v4v6_prefer_v4.exp
@@ -0,0 +1,68 @@
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv http
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http
+ai4: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai5: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai6: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv echo
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv echo
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv echo
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv echo
+ai4: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv echo
+ai5: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv echo
+ai6: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv echo
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv tftp
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv tftp
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv tftp
+ai3: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv tftp
+ai4: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv tftp
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv 80
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http
+ai4: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai5: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai6: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv http
+ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv http
+ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv http
+ai3: flags 0x3 family 28 socktype 5 protocol 132 addrlen 28 host :: serv http
+ai4: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv http
+ai5: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv http
+ai6: flags 0x3 family 2 socktype 5 protocol 132 addrlen 16 host 0.0.0.0 serv http
+
+arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv echo
+ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv echo
+ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv echo
+ai3: flags 0x3 family 28 socktype 5 protocol 132 addrlen 28 host :: serv echo
+ai4: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv echo
+ai5: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv echo
+ai6: flags 0x3 family 2 socktype 5 protocol 132 addrlen 16 host 0.0.0.0 serv echo
+
+arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv tftp
+ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv tftp
+ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv tftp
+ai3: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv tftp
+ai4: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv tftp
+
+arg: flags 0x3 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv 80
+ai1: flags 0x3 family 28 socktype 2 protocol 17 addrlen 28 host :: serv http
+ai2: flags 0x3 family 28 socktype 1 protocol 6 addrlen 28 host :: serv http
+ai3: flags 0x3 family 28 socktype 5 protocol 132 addrlen 28 host :: serv http
+ai4: flags 0x3 family 2 socktype 2 protocol 17 addrlen 16 host 0.0.0.0 serv http
+ai5: flags 0x3 family 2 socktype 1 protocol 6 addrlen 16 host 0.0.0.0 serv http
+ai6: flags 0x3 family 2 socktype 5 protocol 132 addrlen 16 host 0.0.0.0 serv http
+
+arg: flags 0x2 family 0 socktype 1 protocol 0 addrlen 0 host (empty) serv 80
+ai1: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x2 family 0 socktype 2 protocol 0 addrlen 0 host (empty) serv 80
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+
diff --git a/lib/libc/tests/net/getaddrinfo/data/no_serv_v4.exp b/lib/libc/tests/net/getaddrinfo/data/no_serv_v4.exp
new file mode 100644
index 000000000000..667234d2161f
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/no_serv_v4.exp
@@ -0,0 +1,17 @@
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv (empty)
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv 0
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv 0
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv 0
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv (empty)
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv 0
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv 0
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv 0
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv (empty)
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv 0
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv 0
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv 0
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv (empty)
+Name does not resolve
diff --git a/lib/libc/tests/net/getaddrinfo/data/no_serv_v4_only.exp b/lib/libc/tests/net/getaddrinfo/data/no_serv_v4_only.exp
new file mode 100644
index 000000000000..0d28490c8d81
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/no_serv_v4_only.exp
@@ -0,0 +1,20 @@
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv (empty)
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv 0
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv 0
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv 0
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv (empty)
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv 0
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv 0
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv 0
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv (empty)
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv 0
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv 0
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv 0
+ai4: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv 0
+ai5: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv 0
+ai6: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv 0
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv (empty)
+Name does not resolve
diff --git a/lib/libc/tests/net/getaddrinfo/data/no_serv_v4v6.exp b/lib/libc/tests/net/getaddrinfo/data/no_serv_v4v6.exp
new file mode 100644
index 000000000000..53502547c40e
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/no_serv_v4v6.exp
@@ -0,0 +1,20 @@
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv (empty)
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv 0
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv 0
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv 0
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv (empty)
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv 0
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv 0
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv 0
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv (empty)
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv 0
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv 0
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv 0
+ai4: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv 0
+ai5: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv 0
+ai6: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv 0
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv (empty)
+Name does not resolve
diff --git a/lib/libc/tests/net/getaddrinfo/data/no_serv_v4v6_prefer_v4.exp b/lib/libc/tests/net/getaddrinfo/data/no_serv_v4v6_prefer_v4.exp
new file mode 100644
index 000000000000..0d28490c8d81
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/no_serv_v4v6_prefer_v4.exp
@@ -0,0 +1,20 @@
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host ::1 serv (empty)
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv 0
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv 0
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv 0
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host 127.0.0.1 serv (empty)
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv 0
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv 0
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv 0
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host localhost serv (empty)
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv 0
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv 0
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv 0
+ai4: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv 0
+ai5: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv 0
+ai6: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv 0
+
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host (empty) serv (empty)
+Name does not resolve
diff --git a/lib/libc/tests/net/getaddrinfo/data/scoped.exp b/lib/libc/tests/net/getaddrinfo/data/scoped.exp
new file mode 100644
index 000000000000..f5ddb4bf6feb
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/scoped.exp
@@ -0,0 +1,5 @@
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host fe80::1%lo0 serv http
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host fe80::1%lo0 serv http
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host fe80::1%lo0 serv http
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host fe80::1%lo0 serv http
+
diff --git a/lib/libc/tests/net/getaddrinfo/data/scoped_v4_only.exp b/lib/libc/tests/net/getaddrinfo/data/scoped_v4_only.exp
new file mode 100644
index 000000000000..f5ddb4bf6feb
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/scoped_v4_only.exp
@@ -0,0 +1,5 @@
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host fe80::1%lo0 serv http
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host fe80::1%lo0 serv http
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host fe80::1%lo0 serv http
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host fe80::1%lo0 serv http
+
diff --git a/lib/libc/tests/net/getaddrinfo/data/scoped_v4v6_prefer_v4.exp b/lib/libc/tests/net/getaddrinfo/data/scoped_v4v6_prefer_v4.exp
new file mode 100644
index 000000000000..f5ddb4bf6feb
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/scoped_v4v6_prefer_v4.exp
@@ -0,0 +1,5 @@
+arg: flags 0x2 family 0 socktype 0 protocol 0 addrlen 0 host fe80::1%lo0 serv http
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host fe80::1%lo0 serv http
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host fe80::1%lo0 serv http
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host fe80::1%lo0 serv http
+
diff --git a/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4.exp b/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4.exp
new file mode 100644
index 000000000000..021038b81dcc
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4.exp
@@ -0,0 +1,13 @@
+arg: flags 0x2 family 0 socktype 3 protocol 0 addrlen 0 host localhost serv (empty)
+ai1: flags 0x2 family 2 socktype 3 protocol 0 addrlen 16 host 127.0.0.1 serv 0
+
+arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host localhost serv (empty)
+ai1: flags 0x2 family 2 socktype 3 protocol 59 addrlen 16 host 127.0.0.1 serv 0
+
+arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host localhost serv 80
+Service was not recognized for socket type
+arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host localhost serv www
+Service was not recognized for socket type
+arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host ::1 serv (empty)
+ai1: flags 0x2 family 28 socktype 3 protocol 59 addrlen 28 host ::1 serv 0
+
diff --git a/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4_only.exp b/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4_only.exp
new file mode 100644
index 000000000000..932c1faab0d3
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4_only.exp
@@ -0,0 +1,15 @@
+arg: flags 0x2 family 0 socktype 3 protocol 0 addrlen 0 host localhost serv (empty)
+ai1: flags 0x2 family 2 socktype 3 protocol 0 addrlen 16 host 127.0.0.1 serv 0
+ai2: flags 0x2 family 28 socktype 3 protocol 0 addrlen 28 host ::1 serv 0
+
+arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host localhost serv (empty)
+ai1: flags 0x2 family 2 socktype 3 protocol 59 addrlen 16 host 127.0.0.1 serv 0
+ai2: flags 0x2 family 28 socktype 3 protocol 59 addrlen 28 host ::1 serv 0
+
+arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host localhost serv 80
+Service was not recognized for socket type
+arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host localhost serv www
+Service was not recognized for socket type
+arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host ::1 serv (empty)
+ai1: flags 0x2 family 28 socktype 3 protocol 59 addrlen 28 host ::1 serv 0
+
diff --git a/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4v6.exp b/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4v6.exp
new file mode 100644
index 000000000000..e494271a3d35
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4v6.exp
@@ -0,0 +1,15 @@
+arg: flags 0x2 family 0 socktype 3 protocol 0 addrlen 0 host localhost serv (empty)
+ai1: flags 0x2 family 28 socktype 3 protocol 0 addrlen 28 host ::1 serv 0
+ai2: flags 0x2 family 2 socktype 3 protocol 0 addrlen 16 host 127.0.0.1 serv 0
+
+arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host localhost serv (empty)
+ai1: flags 0x2 family 28 socktype 3 protocol 59 addrlen 28 host ::1 serv 0
+ai2: flags 0x2 family 2 socktype 3 protocol 59 addrlen 16 host 127.0.0.1 serv 0
+
+arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host localhost serv 80
+Service was not recognized for socket type
+arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host localhost serv www
+Service was not recognized for socket type
+arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host ::1 serv (empty)
+ai1: flags 0x2 family 28 socktype 3 protocol 59 addrlen 28 host ::1 serv 0
+
diff --git a/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4v6_prefer_v4.exp b/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4v6_prefer_v4.exp
new file mode 100644
index 000000000000..932c1faab0d3
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/sock_raw_v4v6_prefer_v4.exp
@@ -0,0 +1,15 @@
+arg: flags 0x2 family 0 socktype 3 protocol 0 addrlen 0 host localhost serv (empty)
+ai1: flags 0x2 family 2 socktype 3 protocol 0 addrlen 16 host 127.0.0.1 serv 0
+ai2: flags 0x2 family 28 socktype 3 protocol 0 addrlen 28 host ::1 serv 0
+
+arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host localhost serv (empty)
+ai1: flags 0x2 family 2 socktype 3 protocol 59 addrlen 16 host 127.0.0.1 serv 0
+ai2: flags 0x2 family 28 socktype 3 protocol 59 addrlen 28 host ::1 serv 0
+
+arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host localhost serv 80
+Service was not recognized for socket type
+arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host localhost serv www
+Service was not recognized for socket type
+arg: flags 0x2 family 0 socktype 3 protocol 59 addrlen 0 host ::1 serv (empty)
+ai1: flags 0x2 family 28 socktype 3 protocol 59 addrlen 28 host ::1 serv 0
+
diff --git a/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4.exp b/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4.exp
new file mode 100644
index 000000000000..924d2a16f47c
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4.exp
@@ -0,0 +1,7 @@
+arg: flags 0x2 family 2 socktype 0 protocol 0 addrlen 0 host localhost serv http
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x2 family 28 socktype 0 protocol 0 addrlen 0 host localhost serv http
+Address family for hostname not supported
diff --git a/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4_only.exp b/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4_only.exp
new file mode 100644
index 000000000000..af3506938503
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4_only.exp
@@ -0,0 +1,10 @@
+arg: flags 0x2 family 2 socktype 0 protocol 0 addrlen 0 host localhost serv http
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x2 family 28 socktype 0 protocol 0 addrlen 0 host localhost serv http
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http
+
diff --git a/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4v6.exp b/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4v6.exp
new file mode 100644
index 000000000000..af3506938503
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4v6.exp
@@ -0,0 +1,10 @@
+arg: flags 0x2 family 2 socktype 0 protocol 0 addrlen 0 host localhost serv http
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x2 family 28 socktype 0 protocol 0 addrlen 0 host localhost serv http
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http
+
diff --git a/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4v6_prefer_v4.exp b/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4v6_prefer_v4.exp
new file mode 100644
index 000000000000..af3506938503
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/spec_fam_v4v6_prefer_v4.exp
@@ -0,0 +1,10 @@
+arg: flags 0x2 family 2 socktype 0 protocol 0 addrlen 0 host localhost serv http
+ai1: flags 0x2 family 2 socktype 2 protocol 17 addrlen 16 host 127.0.0.1 serv http
+ai2: flags 0x2 family 2 socktype 1 protocol 6 addrlen 16 host 127.0.0.1 serv http
+ai3: flags 0x2 family 2 socktype 5 protocol 132 addrlen 16 host 127.0.0.1 serv http
+
+arg: flags 0x2 family 28 socktype 0 protocol 0 addrlen 0 host localhost serv http
+ai1: flags 0x2 family 28 socktype 2 protocol 17 addrlen 28 host ::1 serv http
+ai2: flags 0x2 family 28 socktype 1 protocol 6 addrlen 28 host ::1 serv http
+ai3: flags 0x2 family 28 socktype 5 protocol 132 addrlen 28 host ::1 serv http
+
diff --git a/lib/libc/tests/net/getaddrinfo/data/unsup_fam.exp b/lib/libc/tests/net/getaddrinfo/data/unsup_fam.exp
new file mode 100644
index 000000000000..69e6b48a854b
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/unsup_fam.exp
@@ -0,0 +1,2 @@
+arg: flags 0x2 family 99 socktype 0 protocol 0 addrlen 0 host localhost serv (empty)
+Address family not recognized
diff --git a/lib/libc/tests/net/getaddrinfo/data/unsup_fam_v4_only.exp b/lib/libc/tests/net/getaddrinfo/data/unsup_fam_v4_only.exp
new file mode 100644
index 000000000000..69e6b48a854b
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/unsup_fam_v4_only.exp
@@ -0,0 +1,2 @@
+arg: flags 0x2 family 99 socktype 0 protocol 0 addrlen 0 host localhost serv (empty)
+Address family not recognized
diff --git a/lib/libc/tests/net/getaddrinfo/data/unsup_fam_v4v6_prefer_v4.exp b/lib/libc/tests/net/getaddrinfo/data/unsup_fam_v4v6_prefer_v4.exp
new file mode 100644
index 000000000000..69e6b48a854b
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/data/unsup_fam_v4v6_prefer_v4.exp
@@ -0,0 +1,2 @@
+arg: flags 0x2 family 99 socktype 0 protocol 0 addrlen 0 host localhost serv (empty)
+Address family not recognized
diff --git a/lib/libc/tests/net/getaddrinfo/getaddrinfo.c b/lib/libc/tests/net/getaddrinfo/getaddrinfo.c
new file mode 100644
index 000000000000..1e066add3119
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/getaddrinfo.c
@@ -0,0 +1,271 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Gleb Smirnoff <glebius@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this 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 <dlfcn.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <resolv.h>
+
+#include <atf-c.h>
+
+static const char goodname[] = "www.freebsd.org";
+static const char goodname_dot[] = "www.freebsd.org.";
+static const char badname[] = "does-not-exist.freebsd.org";
+static const char badname_dot[] = "does-not-exist.freebsd.org.";
+static const char ipv6onlyname[] = "beefy15.nyi.freebsd.org";
+static const char ipv6onlyname_dot[] = "beefy15.nyi.freebsd.org.";
+static const char ipv4onlyname[] = "ipv4only.arpa";
+static const char ipv4onlyname_dot[] = "ipv4only.arpa.";
+/*
+ * We need an IP address that doesn't exist, but not reported with ICMP
+ * unreachable by the nearest router. Let's try TEST-NET-3.
+ */
+static char badresolvconf[] = "nameserver 203.0.113.1";
+static char badresolvconf2[] = "nameserver 203.0.113.1\n"
+ "nameserver 203.0.113.2";
+static char *resconf = NULL;
+FILE *
+fopen(const char * restrict path, const char * restrict mode)
+{
+ static FILE *(*orig)(const char *, const char *);
+
+ if (orig == NULL && (orig = dlsym(RTLD_NEXT, "fopen")) == NULL)
+ atf_libc_error(ENOENT, "dlsym(fopen): %s", dlerror());
+ if (resconf != NULL && strcmp(path, _PATH_RESCONF) == 0)
+ return (fmemopen(resconf, strlen(resconf), mode));
+ else
+ return (orig(path, mode));
+}
+
+static int send_error = 0;
+ssize_t
+send(int s, const void *msg, size_t len, int flags)
+{
+ static ssize_t (*orig)(int, const void *, size_t, int);
+
+ if (orig == NULL && (orig = dlsym(RTLD_NEXT, "send")) == NULL)
+ atf_libc_error(ENOENT, "dlsym(send): %s", dlerror());
+ if (send_error != 0) {
+ errno = send_error;
+ return (-1);
+ } else {
+ return (orig(s, msg, len, flags));
+ }
+}
+
+ATF_TC(basic);
+ATF_TC_HEAD(basic, tc)
+{
+ atf_tc_set_md_var(tc, "require.config", "allow_network_access");
+}
+
+ATF_TC_BODY(basic, tc)
+{
+ static const struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_flags = AI_CANONNAME,
+ };
+ struct addrinfo *res;
+ int rv;
+
+ rv = getaddrinfo(goodname, NULL, &hints, &res);
+ ATF_REQUIRE_MSG(rv == 0,
+ "Expected 0, got %d (%s)", rv, gai_strerror(rv));
+ freeaddrinfo(res);
+
+ rv = getaddrinfo(goodname_dot, NULL, &hints, &res);
+ ATF_REQUIRE_MSG(rv == 0,
+ "Expected 0, got %d (%s)", rv, gai_strerror(rv));
+ freeaddrinfo(res);
+
+ rv = getaddrinfo(badname, NULL, &hints, &res);
+ ATF_REQUIRE_MSG(rv == EAI_NONAME,
+ "Expected %d (EAI_NONAME), got %d (%s)",
+ EAI_NONAME, rv, gai_strerror(rv));
+
+ rv = getaddrinfo(badname_dot, NULL, &hints, &res);
+ ATF_REQUIRE_MSG(rv == EAI_NONAME,
+ "Expected %d (EAI_NONAME), got %d (%s)",
+ EAI_NONAME, rv, gai_strerror(rv));
+}
+
+ATF_TC_WITHOUT_HEAD(timeout);
+ATF_TC_BODY(timeout, tc)
+{
+ static const struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_flags = AI_CANONNAME,
+ };
+ struct addrinfo *res;
+ int rv;
+
+ resconf = badresolvconf;
+ rv = getaddrinfo(goodname, NULL, &hints, &res);
+ ATF_REQUIRE_MSG(rv == EAI_AGAIN,
+ "Expected %d (EAI_AGAIN), got %d (%s)",
+ EAI_AGAIN, rv, gai_strerror(rv));
+
+ rv = getaddrinfo(goodname_dot, NULL, &hints, &res);
+ ATF_REQUIRE_MSG(rv == EAI_AGAIN,
+ "Expected %d (EAI_AGAIN), got %d (%s)",
+ EAI_AGAIN, rv, gai_strerror(rv));
+}
+
+ATF_TC_WITHOUT_HEAD(timeout_specific);
+ATF_TC_BODY(timeout_specific, tc)
+{
+ static const struct addrinfo hints = {
+ .ai_family = AF_INET,
+ .ai_socktype = SOCK_STREAM,
+ .ai_flags = AI_CANONNAME,
+ };
+ struct addrinfo *res;
+ int rv;
+
+ resconf = badresolvconf;
+ rv = getaddrinfo(goodname, "666", &hints, &res);
+ ATF_REQUIRE_MSG(rv == EAI_AGAIN,
+ "Expected %d (EAI_AGAIN), got %d (%s)",
+ EAI_AGAIN, rv, gai_strerror(rv));
+
+ rv = getaddrinfo(goodname_dot, "666", &hints, &res);
+ ATF_REQUIRE_MSG(rv == EAI_AGAIN,
+ "Expected %d (EAI_AGAIN), got %d (%s)",
+ EAI_AGAIN, rv, gai_strerror(rv));
+}
+
+ATF_TC_WITHOUT_HEAD(timeout2);
+ATF_TC_BODY(timeout2, tc)
+{
+ static const struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_flags = AI_CANONNAME,
+ };
+ struct addrinfo *res;
+ int rv;
+
+ resconf = badresolvconf2;
+ rv = getaddrinfo(goodname, NULL, &hints, &res);
+ ATF_REQUIRE_MSG(rv == EAI_AGAIN,
+ "Expected %d (EAI_AGAIN), got %d (%s)",
+ EAI_AGAIN, rv, gai_strerror(rv));
+
+ rv = getaddrinfo(goodname_dot, NULL, &hints, &res);
+ ATF_REQUIRE_MSG(rv == EAI_AGAIN,
+ "Expected %d (EAI_AGAIN), got %d (%s)",
+ EAI_AGAIN, rv, gai_strerror(rv));
+}
+
+/*
+ * Emulate interface/network down.
+ */
+ATF_TC_WITHOUT_HEAD(netdown);
+ATF_TC_BODY(netdown, tc)
+{
+ static const struct addrinfo hints = {
+ .ai_family = AF_UNSPEC,
+ .ai_flags = AI_CANONNAME,
+ };
+ struct addrinfo *res;
+ int rv;
+
+ send_error = ENETDOWN;
+ rv = getaddrinfo(goodname, NULL, &hints, &res);
+ ATF_REQUIRE_MSG(rv == EAI_AGAIN,
+ "Expected %d (EAI_AGAIN), got %d (%s)",
+ EAI_AGAIN, rv, gai_strerror(rv));
+
+ rv = getaddrinfo(goodname_dot, NULL, &hints, &res);
+ ATF_REQUIRE_MSG(rv == EAI_AGAIN,
+ "Expected %d (EAI_AGAIN), got %d (%s)",
+ EAI_AGAIN, rv, gai_strerror(rv));
+}
+
+/*
+ * See https://reviews.freebsd.org/D37139.
+ */
+ATF_TC(nofamily);
+ATF_TC_HEAD(nofamily, tc)
+{
+ atf_tc_set_md_var(tc, "require.config", "allow_network_access");
+}
+ATF_TC_BODY(nofamily, tc)
+{
+ static const struct addrinfo hints4 = {
+ .ai_family = AF_INET,
+ .ai_flags = AI_CANONNAME,
+ }, hints6 = {
+ .ai_family = AF_INET6,
+ .ai_flags = AI_CANONNAME,
+ };
+ struct addrinfo *res;
+ int rv;
+
+ rv = getaddrinfo(ipv6onlyname, NULL, &hints4, &res);
+ ATF_REQUIRE_MSG(rv == EAI_ADDRFAMILY,
+ "Expected %d (EAI_ADDRFAMILY), got %d (%s)",
+ EAI_ADDRFAMILY, rv, gai_strerror(rv));
+
+ rv = getaddrinfo(ipv6onlyname_dot, NULL, &hints4, &res);
+ ATF_REQUIRE_MSG(rv == EAI_ADDRFAMILY,
+ "Expected %d (EAI_ADDRFAMILY), got %d (%s)",
+ EAI_ADDRFAMILY, rv, gai_strerror(rv));
+
+ rv = getaddrinfo(ipv4onlyname, NULL, &hints6, &res);
+ ATF_REQUIRE_MSG(rv == EAI_ADDRFAMILY,
+ "Expected %d (EAI_ADDRFAMILY), got %d (%s)",
+ EAI_ADDRFAMILY, rv, gai_strerror(rv));
+
+ rv = getaddrinfo(ipv4onlyname_dot, NULL, &hints6, &res);
+ ATF_REQUIRE_MSG(rv == EAI_ADDRFAMILY,
+ "Expected %d (EAI_ADDRFAMILY), got %d (%s)",
+ EAI_ADDRFAMILY, rv, gai_strerror(rv));
+
+ rv = getaddrinfo(badname, NULL, &hints4, &res);
+ ATF_REQUIRE_MSG(rv == EAI_NONAME,
+ "Expected %d (EAI_NONAME), got %d (%s)",
+ EAI_NONAME, rv, gai_strerror(rv));
+
+ rv = getaddrinfo(badname_dot, NULL, &hints6, &res);
+ ATF_REQUIRE_MSG(rv == EAI_NONAME,
+ "Expected %d (EAI_NONAME), got %d (%s)",
+ EAI_NONAME, rv, gai_strerror(rv));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, basic);
+ ATF_TP_ADD_TC(tp, timeout);
+ ATF_TP_ADD_TC(tp, timeout_specific);
+ ATF_TP_ADD_TC(tp, timeout2);
+ ATF_TP_ADD_TC(tp, netdown);
+ ATF_TP_ADD_TC(tp, nofamily);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/net/getaddrinfo/getaddrinfo_test.sh b/lib/libc/tests/net/getaddrinfo/getaddrinfo_test.sh
new file mode 100755
index 000000000000..dd17ab2e3f4a
--- /dev/null
+++ b/lib/libc/tests/net/getaddrinfo/getaddrinfo_test.sh
@@ -0,0 +1,443 @@
+# $NetBSD: t_getaddrinfo.sh,v 1.2 2011/06/15 07:54:32 jmmv Exp $
+
+#
+# Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, and 2002 WIDE 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. Neither the name of the project 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 PROJECT AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (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 [ "$(sysctl -i -n kern.features.vimage)" != 1 ]; then
+ atf_skip "This test requires VIMAGE"
+fi
+
+vnet_mkjail()
+{
+ jailname=getaddrinfo_test_$1
+ jail -c name=${jailname} persist vnet
+ ifconfig -j ${jailname} lo0 inet 127.0.0.1/8
+ # For those machines not support IPv6
+ ifconfig -j ${jailname} lo0 inet6 ::1/64 || true
+ service -j ${jailname} ip6addrctl $2 || true
+}
+
+vnet_cleanup()
+{
+ jailname=getaddrinfo_test_$1
+ jail -r ${jailname}
+}
+
+check_output()
+{
+ if [ "$2" = "none" ]; then
+ if [ "$3" = "prefer_v6" ]; then
+ exp="${1}.exp"
+ else
+ exp="${1}_v4_only.exp"
+ fi
+ elif [ "$2" = "hosts" ]; then
+ lcl=$(cat /etc/hosts | sed -e 's/#.*$//' -e 's/[ ][ ]*/ /g' | awk '/ localhost($| )/ {printf "%s ", $1}')
+ if [ "${lcl%*::*}" = "${lcl}" ]; then
+ exp="${1}_v4_only.exp"
+ else
+ if [ "$3" = "prefer_v6" ]; then
+ exp="${1}_v4v6.exp"
+ else
+ exp="${1}_v4v6_prefer_v4.exp"
+ fi
+ fi
+ elif [ "$2" = "ifconfig" ]; then
+ lcl=$(ifconfig lo0 | grep inet6)
+ if [ -n "${lcl}" ]; then
+ if [ "$3" = "prefer_v6" ]; then
+ exp="${1}_v4v6.exp"
+ else
+ exp="${1}_v4v6_prefer_v4.exp"
+ fi
+ else
+ exp="${1}_v4_only.exp"
+ fi
+ else
+ atf_fail "Invalid family_match_type $2 requested."
+ fi
+
+ cmp -s "$(atf_get_srcdir)/data/${exp}" out && return
+ diff -u "$(atf_get_srcdir)/data/${exp}" out || atf_fail "Actual output does not match expected output"
+}
+
+atf_test_case basic_prefer_v4 cleanup
+basic_prefer_v4_head()
+{
+ atf_set "descr" "Testing basic ones with prefer_v4"
+ atf_set "require.user" "root"
+}
+basic_prefer_v4_body()
+{
+ vnet_mkjail basic_prefer_v4 prefer_ipv4
+ TEST="jexec getaddrinfo_test_basic_prefer_v4 $(atf_get_srcdir)/h_gai"
+
+ ( $TEST ::1 http
+ $TEST 127.0.0.1 http
+ $TEST localhost http
+ $TEST ::1 tftp
+ $TEST 127.0.0.1 tftp
+ $TEST localhost tftp
+ $TEST ::1 echo
+ $TEST 127.0.0.1 echo
+ $TEST localhost echo ) > out 2>&1
+
+ check_output basics hosts prefer_v4
+}
+basic_prefer_v4_cleanup()
+{
+ vnet_cleanup basic_prefer_v4
+}
+
+atf_test_case basic cleanup
+basic_head()
+{
+ atf_set "descr" "Testing basic ones with prefer_v6"
+ atf_set "require.user" "root"
+}
+basic_body()
+{
+ vnet_mkjail basic prefer_ipv6
+ TEST="jexec getaddrinfo_test_basic $(atf_get_srcdir)/h_gai"
+
+ ( $TEST ::1 http
+ $TEST 127.0.0.1 http
+ $TEST localhost http
+ $TEST ::1 tftp
+ $TEST 127.0.0.1 tftp
+ $TEST localhost tftp
+ $TEST ::1 echo
+ $TEST 127.0.0.1 echo
+ $TEST localhost echo ) > out 2>&1
+
+ check_output basics ifconfig prefer_v6
+}
+basic_cleanup()
+{
+ vnet_cleanup basic
+}
+
+atf_test_case specific_prefer_v4 cleanup
+specific_prefer_v4_head()
+{
+ atf_set "descr" "Testing specific address family with prefer_v4"
+ atf_set "require.user" "root"
+}
+specific_prefer_v4_body()
+{
+ vnet_mkjail specific_prefer_v4 prefer_ipv4
+ TEST="jexec getaddrinfo_test_specific_prefer_v4 $(atf_get_srcdir)/h_gai"
+
+ ( $TEST -4 localhost http
+ $TEST -6 localhost http ) > out 2>&1
+
+ check_output spec_fam hosts prefer_v4
+}
+specific_prefer_v4_cleanup()
+{
+ vnet_cleanup specific_prefer_v4
+}
+
+atf_test_case specific cleanup
+specific_head()
+{
+ atf_set "descr" "Testing specific address family with prefer_v6"
+ atf_set "require.user" "root"
+}
+specific_body()
+{
+ vnet_mkjail specific prefer_ipv6
+ TEST="jexec getaddrinfo_test_specific $(atf_get_srcdir)/h_gai"
+
+ ( $TEST -4 localhost http
+ $TEST -6 localhost http ) > out 2>&1
+
+ check_output spec_fam hosts prefer_v6
+}
+specific_cleanup()
+{
+ vnet_cleanup specific
+}
+
+atf_test_case empty_hostname_prefer_v4 cleanup
+empty_hostname_prefer_v4_head()
+{
+ atf_set "descr" "Testing empty hostname with prefer_v4"
+ atf_set "require.user" "root"
+}
+empty_hostname_prefer_v4_body()
+{
+ vnet_mkjail empty_hostname_prefer_v4 prefer_ipv4
+ TEST="jexec getaddrinfo_test_empty_hostname_prefer_v4 $(atf_get_srcdir)/h_gai"
+
+ ( $TEST '' http
+ $TEST '' echo
+ $TEST '' tftp
+ $TEST '' 80
+ $TEST -P '' http
+ $TEST -P '' echo
+ $TEST -P '' tftp
+ $TEST -P '' 80
+ $TEST -S '' 80
+ $TEST -D '' 80 ) > out 2>&1
+
+ check_output no_host ifconfig prefer_v4
+}
+empty_hostname_prefer_v4_cleanup()
+{
+ vnet_cleanup empty_hostname_prefer_v4
+}
+
+atf_test_case empty_hostname cleanup
+empty_hostname_head()
+{
+ atf_set "descr" "Testing empty hostname with prefer_v6"
+ atf_set "require.user" "root"
+}
+empty_hostname_body()
+{
+ vnet_mkjail empty_hostname prefer_ipv6
+ TEST="jexec getaddrinfo_test_empty_hostname $(atf_get_srcdir)/h_gai"
+
+ ( $TEST '' http
+ $TEST '' echo
+ $TEST '' tftp
+ $TEST '' 80
+ $TEST -P '' http
+ $TEST -P '' echo
+ $TEST -P '' tftp
+ $TEST -P '' 80
+ $TEST -S '' 80
+ $TEST -D '' 80 ) > out 2>&1
+
+ check_output no_host ifconfig prefer_v6
+}
+empty_hostname_cleanup()
+{
+ vnet_cleanup empty_hostname
+}
+
+atf_test_case empty_servname_prefer_v4 cleanup
+empty_servname_prefer_v4_head()
+{
+ atf_set "descr" "Testing empty service name with prefer_v4"
+ atf_set "require.user" "root"
+}
+empty_servname_prefer_v4_body()
+{
+ vnet_mkjail empty_servname_prefer_v4 prefer_ipv4
+ TEST="jexec getaddrinfo_test_empty_servname_prefer_v4 $(atf_get_srcdir)/h_gai"
+
+ ( $TEST ::1 ''
+ $TEST 127.0.0.1 ''
+ $TEST localhost ''
+ $TEST '' '' ) > out 2>&1
+
+ check_output no_serv hosts prefer_v4
+}
+empty_servname_prefer_v4_cleanup()
+{
+ vnet_cleanup empty_servname_prefer_v4
+}
+
+atf_test_case empty_servname cleanup
+empty_servname_head()
+{
+ atf_set "descr" "Testing empty service name with prefer_v6"
+ atf_set "require.user" "root"
+}
+empty_servname_body()
+{
+ vnet_mkjail empty_servname prefer_ipv6
+ TEST="jexec getaddrinfo_test_empty_servname $(atf_get_srcdir)/h_gai"
+
+ ( $TEST ::1 ''
+ $TEST 127.0.0.1 ''
+ $TEST localhost ''
+ $TEST '' '' ) > out 2>&1
+
+ check_output no_serv ifconfig prefer_v6
+}
+empty_servname_cleanup()
+{
+ vnet_cleanup empty_servname
+}
+
+atf_test_case sock_raw_prefer_v4 cleanup
+sock_raw_prefer_v4_head()
+{
+ atf_set "descr" "Testing raw socket with prefer_v4"
+ atf_set "require.user" "root"
+}
+sock_raw_prefer_v4_body()
+{
+ vnet_mkjail sock_raw_prefer_v4 prefer_ipv4
+ TEST="jexec getaddrinfo_test_sock_raw_prefer_v4 $(atf_get_srcdir)/h_gai"
+
+ ( $TEST -R -p 0 localhost ''
+ $TEST -R -p 59 localhost ''
+ $TEST -R -p 59 localhost 80
+ $TEST -R -p 59 localhost www
+ $TEST -R -p 59 ::1 '' ) > out 2>&1
+
+ check_output sock_raw hosts prefer_v4
+}
+sock_raw_prefer_v4_cleanup()
+{
+ vnet_cleanup sock_raw_prefer_v4
+}
+
+atf_test_case sock_raw cleanup
+sock_raw_head()
+{
+ atf_set "descr" "Testing raw socket with prefer_v6"
+ atf_set "require.user" "root"
+}
+sock_raw_body()
+{
+ vnet_mkjail sock_raw prefer_ipv6
+ TEST="jexec getaddrinfo_test_sock_raw $(atf_get_srcdir)/h_gai"
+
+ ( $TEST -R -p 0 localhost ''
+ $TEST -R -p 59 localhost ''
+ $TEST -R -p 59 localhost 80
+ $TEST -R -p 59 localhost www
+ $TEST -R -p 59 ::1 '' ) > out 2>&1
+
+ check_output sock_raw ifconfig prefer_v6
+}
+sock_raw_cleanup()
+{
+ vnet_cleanup sock_raw
+}
+
+atf_test_case unsupported_family_prefer_v4 cleanup
+unsupported_family_prefer_v4_head()
+{
+ atf_set "descr" "Testing unsupported family with prefer_v4"
+ atf_set "require.user" "root"
+}
+unsupported_family_prefer_v4_body()
+{
+ vnet_mkjail unsupported_family_prefer_v4 prefer_ipv4
+ TEST="jexec getaddrinfo_test_unsupported_family_prefer_v4 $(atf_get_srcdir)/h_gai"
+
+ ( $TEST -f 99 localhost '' ) > out 2>&1
+
+ check_output unsup_fam ifconfig prefer_v4
+}
+unsupported_family_prefer_v4_cleanup()
+{
+ vnet_cleanup unsupported_family_prefer_v4
+}
+
+atf_test_case unsupported_family cleanup
+unsupported_family_head()
+{
+ atf_set "descr" "Testing unsupported family with prefer_v6"
+ atf_set "require.user" "root"
+}
+unsupported_family_body()
+{
+ vnet_mkjail unsupported_family prefer_ipv6
+ TEST="jexec getaddrinfo_test_unsupported_family $(atf_get_srcdir)/h_gai"
+
+ ( $TEST -f 99 localhost '' ) > out 2>&1
+
+ check_output unsup_fam none prefer_v6
+}
+unsupported_family_cleanup()
+{
+ vnet_cleanup unsupported_family
+}
+
+atf_test_case scopeaddr_prefer_v4 cleanup
+scopeaddr_prefer_v4_head()
+{
+ atf_set "descr" "Testing scoped address format with prefer_v4"
+ atf_set "require.user" "root"
+}
+scopeaddr_prefer_v4_body()
+{
+ vnet_mkjail scopeaddr_prefer_v4 prefer_ipv4
+ TEST="jexec getaddrinfo_test_scopeaddr_prefer_v4 $(atf_get_srcdir)/h_gai"
+
+ ( $TEST fe80::1%lo0 http
+# IF=ifconfig -a | grep -v '^ ' | sed -e 's/:.*//' | head -1 | awk '{print $1}'
+# $TEST fe80::1%$IF http
+ ) > out 2>&1
+
+ check_output scoped ifconfig prefer_v4
+}
+scopeaddr_prefer_v4_cleanup()
+{
+ vnet_cleanup scopeaddr_prefer_v4
+}
+
+atf_test_case scopeaddr cleanup
+scopeaddr_head()
+{
+ atf_set "descr" "Testing scoped address format with prefer_v6"
+ atf_set "require.user" "root"
+}
+scopeaddr_body()
+{
+ vnet_mkjail scopeaddr prefer_ipv6
+ TEST="jexec getaddrinfo_test_scopeaddr $(atf_get_srcdir)/h_gai"
+
+ ( $TEST fe80::1%lo0 http
+# IF=ifconfig -a | grep -v '^ ' | sed -e 's/:.*//' | head -1 | awk '{print $1}'
+# $TEST fe80::1%$IF http
+ ) > out 2>&1
+
+ check_output scoped none prefer_v6
+}
+scopeaddr_cleanup()
+{
+ vnet_cleanup scopeaddr
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case basic_prefer_v4
+ atf_add_test_case specific_prefer_v4
+ atf_add_test_case empty_hostname_prefer_v4
+ atf_add_test_case empty_servname_prefer_v4
+ atf_add_test_case sock_raw_prefer_v4
+ atf_add_test_case unsupported_family_prefer_v4
+ atf_add_test_case scopeaddr_prefer_v4
+
+ atf_add_test_case basic
+ atf_add_test_case specific
+ atf_add_test_case empty_hostname
+ atf_add_test_case empty_servname
+ atf_add_test_case sock_raw
+ atf_add_test_case unsupported_family
+ atf_add_test_case scopeaddr
+}
diff --git a/lib/libc/tests/net/link_addr_test.cc b/lib/libc/tests/net/link_addr_test.cc
new file mode 100644
index 000000000000..b973b924dc13
--- /dev/null
+++ b/lib/libc/tests/net/link_addr_test.cc
@@ -0,0 +1,532 @@
+/*
+ * Copyright (c) 2025 Lexi Winter
+ *
+ * SPDX-License-Identifier: ISC
+ */
+
+/*
+ * Tests for link_addr() and link_ntoa().
+ *
+ * link_addr converts a string representing an (optionally null) interface name
+ * followed by an Ethernet address into a sockaddr_dl. The expected format is
+ * "[ifname]:lladdr". This means if ifname is not specified, the leading colon
+ * is still required.
+ *
+ * link_ntoa does the inverse of link_addr, returning a string representation
+ * of the address.
+ *
+ * Note that the output format of link_ntoa is not valid input for link_addr
+ * since the leading colon may be omitted. This is by design.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <net/ethernet.h>
+#include <net/if_dl.h>
+
+#include <format>
+#include <iostream>
+#include <ranges>
+#include <span>
+#include <utility>
+#include <vector>
+
+#include <cstddef>
+#include <cstdint>
+
+#include <atf-c++.hpp>
+
+using namespace std::literals;
+
+/*
+ * Define operator== and operator<< for ether_addr so we can use them in
+ * ATF_EXPECT_EQ expressions.
+ */
+
+bool
+operator==(ether_addr a, ether_addr b)
+{
+ return (std::ranges::equal(a.octet, b.octet));
+}
+
+std::ostream &
+operator<<(std::ostream &s, ether_addr a)
+{
+ for (unsigned i = 0; i < ETHER_ADDR_LEN; ++i) {
+ if (i > 0)
+ s << ":";
+
+ s << std::format("{:02x}", static_cast<int>(a.octet[i]));
+ }
+
+ return (s);
+}
+
+/*
+ * Create a sockaddr_dl from a string using link_addr(), and ensure the
+ * returned struct looks valid.
+ */
+sockaddr_dl
+make_linkaddr(const std::string &addr)
+{
+ auto sdl = sockaddr_dl{};
+ int ret;
+
+ sdl.sdl_len = sizeof(sdl);
+ ret = ::link_addr(addr.c_str(), &sdl);
+
+ ATF_REQUIRE_EQ(0, ret);
+ ATF_REQUIRE_EQ(AF_LINK, static_cast<int>(sdl.sdl_family));
+ ATF_REQUIRE_EQ(true, sdl.sdl_len > 0);
+ ATF_REQUIRE_EQ(true, sdl.sdl_nlen >= 0);
+
+ return (sdl);
+}
+
+/*
+ * Return the data stored in a sockaddr_dl as a span.
+ */
+std::span<const char>
+data(const sockaddr_dl &sdl)
+{
+ // sdl_len is the entire structure, but we only want the length of the
+ // data area.
+ auto dlen = sdl.sdl_len - offsetof(sockaddr_dl, sdl_data);
+ return {&sdl.sdl_data[0], dlen};
+}
+
+/*
+ * Return the interface name stored in a sockaddr_dl as a string.
+ */
+std::string_view
+ifname(const sockaddr_dl &sdl)
+{
+ auto name = data(sdl).subspan(0, sdl.sdl_nlen);
+ return {name.begin(), name.end()};
+}
+
+/*
+ * Return the Ethernet address stored in a sockaddr_dl as an ether_addr.
+ */
+ether_addr
+addr(const sockaddr_dl &sdl)
+{
+ ether_addr ret{};
+ ATF_REQUIRE_EQ(ETHER_ADDR_LEN, sdl.sdl_alen);
+ std::ranges::copy(data(sdl).subspan(sdl.sdl_nlen, ETHER_ADDR_LEN),
+ &ret.octet[0]);
+ return (ret);
+}
+
+/*
+ * Return the link address stored in a sockaddr_dl as a span of octets.
+ */
+std::span<const std::uint8_t>
+lladdr(const sockaddr_dl &sdl)
+{
+ auto data = reinterpret_cast<const uint8_t *>(LLADDR(&sdl));
+ return {data, data + sdl.sdl_alen};
+}
+
+
+/*
+ * Some sample addresses we use for testing. Include at least one address for
+ * each format we want to support.
+ */
+
+struct test_address {
+ std::string input; /* value passed to link_addr */
+ std::string ntoa; /* expected return from link_ntoa */
+ ether_addr addr{}; /* expected return from link_addr */
+};
+
+std::vector<test_address> test_addresses{
+ // No delimiter
+ {"001122334455"s, "0.11.22.33.44.55",
+ ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}},
+
+ // Colon delimiter
+ {"00:11:22:33:44:55"s, "0.11.22.33.44.55",
+ ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}},
+
+ // Dash delimiter
+ {"00-11-22-33-44-55"s, "0.11.22.33.44.55",
+ ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}},
+
+ // Period delimiter (link_ntoa format)
+ {"00.11.22.33.44.55"s, "0.11.22.33.44.55",
+ ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}},
+
+ // Period delimiter (Cisco format)
+ {"0011.2233.4455"s, "0.11.22.33.44.55",
+ ether_addr{0x00, 0x11, 0x22, 0x33, 0x44, 0x55}},
+
+ // An addresses without leading zeroes.
+ {"0:1:02:30:4:55"s, "0.1.2.30.4.55",
+ ether_addr{0x00, 0x01, 0x02, 0x30, 0x04, 0x55}},
+
+ // An address with some uppercase letters.
+ {"AA:B:cC:Dd:e0:1f"s, "aa.b.cc.dd.e0.1f",
+ ether_addr{0xaa, 0x0b, 0xcc, 0xdd, 0xe0, 0x1f}},
+
+ // Addresses composed only of letters, to make sure they're not
+ // confused with an interface name.
+
+ {"aabbccddeeff"s, "aa.bb.cc.dd.ee.ff",
+ ether_addr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}},
+
+ {"aa:bb:cc:dd:ee:ff"s, "aa.bb.cc.dd.ee.ff",
+ ether_addr{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}},
+
+ // Address with a blank octet; this is an old form of Ethernet address.
+ {"00:11::33:44:55"s, "0.11.0.33.44.55",
+ ether_addr{0x00, 0x11, 0x00, 0x33, 0x44, 0x55}},
+};
+
+/*
+ * Test without an interface name.
+ */
+ATF_TEST_CASE_WITHOUT_HEAD(basic)
+ATF_TEST_CASE_BODY(basic)
+{
+ for (const auto &ta : test_addresses) {
+ // This does basic tests on the returned value.
+ auto sdl = make_linkaddr(":" + ta.input);
+
+ // Check the ifname and address.
+ ATF_REQUIRE_EQ(""s, ifname(sdl));
+ ATF_REQUIRE_EQ(ETHER_ADDR_LEN, static_cast<int>(sdl.sdl_alen));
+ ATF_REQUIRE_EQ(ta.addr, addr(sdl));
+
+ // Check link_ntoa returns the expected value.
+ auto ntoa = std::string(::link_ntoa(&sdl));
+ ATF_REQUIRE_EQ(ta.ntoa, ntoa);
+ }
+
+}
+
+/*
+ * Test with an interface name.
+ */
+ATF_TEST_CASE_WITHOUT_HEAD(ifname)
+ATF_TEST_CASE_BODY(ifname)
+{
+ for (const auto &ta : test_addresses) {
+ auto sdl = make_linkaddr("ix0:" + ta.input);
+
+ ATF_REQUIRE_EQ("ix0", ifname(sdl));
+ ATF_REQUIRE_EQ(ETHER_ADDR_LEN, static_cast<int>(sdl.sdl_alen));
+ ATF_REQUIRE_EQ(ta.addr, addr(sdl));
+
+ auto ntoa = std::string(::link_ntoa(&sdl));
+ ATF_REQUIRE_EQ("ix0:" + ta.ntoa, ntoa);
+ }
+
+}
+
+/*
+ * Test with some invalid addresses.
+ */
+ATF_TEST_CASE_WITHOUT_HEAD(invalid)
+ATF_TEST_CASE_BODY(invalid)
+{
+ auto const invalid_addresses = std::vector{
+ // Invalid separator
+ ":1/2/3"s,
+ "ix0:1/2/3"s,
+
+ // Multiple different separators
+ ":1.2-3"s,
+ "ix0:1.2-3"s,
+
+ // An IP address
+ ":10.1.2.200/28"s,
+ "ix0:10.1.2.200/28"s,
+
+ // Valid address followed by garbage
+ ":1.2.3xxx"s,
+ ":1.2.3.xxx"s,
+ "ix0:1.2.3xxx"s,
+ "ix0:1.2.3.xxx"s,
+ };
+
+ for (auto const &addr : invalid_addresses) {
+ int ret;
+
+ auto sdl = sockaddr_dl{};
+ sdl.sdl_len = sizeof(sdl);
+
+ ret = link_addr(addr.c_str(), &sdl);
+ ATF_REQUIRE_EQ(-1, ret);
+ }
+}
+
+/*
+ * Test some non-Ethernet addresses.
+ */
+ATF_TEST_CASE_WITHOUT_HEAD(nonether)
+ATF_TEST_CASE_BODY(nonether)
+{
+ sockaddr_dl sdl;
+
+ /* A short address */
+ sdl = make_linkaddr(":1:23:cc");
+ ATF_REQUIRE_EQ("", ifname(sdl));
+ ATF_REQUIRE_EQ("1.23.cc"s, ::link_ntoa(&sdl));
+ ATF_REQUIRE_EQ(3, sdl.sdl_alen);
+ ATF_REQUIRE_EQ(true,
+ std::ranges::equal(std::vector{0x01u, 0x23u, 0xccu}, lladdr(sdl)));
+
+ /* A long address */
+ sdl = make_linkaddr(":1:23:cc:a:b:c:d:e:f");
+ ATF_REQUIRE_EQ("", ifname(sdl));
+ ATF_REQUIRE_EQ("1.23.cc.a.b.c.d.e.f"s, ::link_ntoa(&sdl));
+ ATF_REQUIRE_EQ(9, sdl.sdl_alen);
+ ATF_REQUIRE_EQ(true, std::ranges::equal(
+ std::vector{0x01u, 0x23u, 0xccu, 0xau, 0xbu, 0xcu, 0xdu, 0xeu, 0xfu},
+ lladdr(sdl)));
+}
+
+/*
+ * Test link_addr behaviour with undersized buffers.
+ */
+ATF_TEST_CASE_WITHOUT_HEAD(smallbuf)
+ATF_TEST_CASE_BODY(smallbuf)
+{
+ static constexpr auto garbage = std::byte{0xcc};
+ auto buf = std::vector<std::byte>();
+ sockaddr_dl *sdl;
+ int ret;
+
+ /*
+ * Make an sdl with an sdl_data member of the appropriate size, and
+ * place it in buf. Ensure it's followed by a trailing byte of garbage
+ * so we can test that link_addr doesn't write past the end.
+ */
+ auto mksdl = [&buf](std::size_t datalen) -> sockaddr_dl * {
+ auto actual_size = datalen + offsetof(sockaddr_dl, sdl_data);
+
+ buf.resize(actual_size + 1);
+ std::ranges::fill(buf, garbage);
+
+ auto *sdl = new(reinterpret_cast<sockaddr_dl *>(&buf[0]))
+ sockaddr_dl;
+ sdl->sdl_len = actual_size;
+ return (sdl);
+ };
+
+ /* An sdl large enough to store the interface name */
+ sdl = mksdl(3);
+ ret = link_addr("ix0:1.2.3", sdl);
+ ATF_REQUIRE(*rbegin(buf) == garbage);
+ ATF_REQUIRE_EQ(-1, ret);
+ ATF_REQUIRE_EQ(ENOSPC, errno);
+ ATF_REQUIRE_EQ(3, sdl->sdl_nlen);
+ ATF_REQUIRE_EQ("ix0", ifname(*sdl));
+ ATF_REQUIRE_EQ(0, static_cast<int>(sdl->sdl_alen));
+
+ /*
+ * For these tests, test both with and without an interface name, since
+ * that will overflow the buffer in different places.
+ */
+
+ /* An empty sdl. Nothing may grow on this cursed ground. */
+
+ sdl = mksdl(0);
+ ret = link_addr("ix0:1.2.3", sdl);
+ ATF_REQUIRE(*rbegin(buf) == garbage);
+ ATF_REQUIRE_EQ(-1, ret);
+ ATF_REQUIRE_EQ(ENOSPC, errno);
+ ATF_REQUIRE_EQ(0, sdl->sdl_nlen);
+ ATF_REQUIRE_EQ(0, static_cast<int>(sdl->sdl_alen));
+
+ sdl = mksdl(0);
+ ret = link_addr(":1.2.3", sdl);
+ ATF_REQUIRE(*rbegin(buf) == garbage);
+ ATF_REQUIRE_EQ(-1, ret);
+ ATF_REQUIRE_EQ(ENOSPC, errno);
+ ATF_REQUIRE_EQ(0, sdl->sdl_nlen);
+ ATF_REQUIRE_EQ(0, static_cast<int>(sdl->sdl_alen));
+
+ /*
+ * An sdl large enough to store the interface name and two octets of the
+ * address.
+ */
+
+ sdl = mksdl(5);
+ ret = link_addr("ix0:1.2.3", sdl);
+ ATF_REQUIRE(*rbegin(buf) == garbage);
+ ATF_REQUIRE_EQ(-1, ret);
+ ATF_REQUIRE_EQ(ENOSPC, errno);
+ ATF_REQUIRE_EQ("ix0", ifname(*sdl));
+ ATF_REQUIRE(std::ranges::equal(
+ std::vector{ 0x01, 0x02 }, lladdr(*sdl)));
+
+ sdl = mksdl(2);
+ ret = link_addr(":1.2.3", sdl);
+ ATF_REQUIRE(*rbegin(buf) == garbage);
+ ATF_REQUIRE_EQ(-1, ret);
+ ATF_REQUIRE_EQ(ENOSPC, errno);
+ ATF_REQUIRE_EQ("", ifname(*sdl));
+ ATF_REQUIRE(std::ranges::equal(
+ std::vector{ 0x01, 0x02 }, lladdr(*sdl)));
+
+ /*
+ * An sdl large enough to store the entire address.
+ */
+
+ sdl = mksdl(6);
+ ret = link_addr("ix0:1.2.3", sdl);
+ ATF_REQUIRE(*rbegin(buf) == garbage);
+ ATF_REQUIRE_EQ(0, ret);
+ ATF_REQUIRE_EQ("ix0", ifname(*sdl));
+ ATF_REQUIRE(std::ranges::equal(
+ std::vector{ 0x01, 0x02, 0x03 }, lladdr(*sdl)));
+
+ sdl = mksdl(3);
+ ret = link_addr(":1.2.3", sdl);
+ ATF_REQUIRE(*rbegin(buf) == garbage);
+ ATF_REQUIRE_EQ(0, ret);
+ ATF_REQUIRE_EQ("", ifname(*sdl));
+ ATF_REQUIRE(std::ranges::equal(
+ std::vector{ 0x01, 0x02, 0x03 }, lladdr(*sdl)));
+}
+
+/*
+ * Test an extremely long address which would overflow link_ntoa's internal
+ * buffer. It should handle this by truncating the output.
+ * (Test for SA-16:37.libc / CVE-2016-6559.)
+ */
+ATF_TEST_CASE_WITHOUT_HEAD(overlong)
+ATF_TEST_CASE_BODY(overlong)
+{
+ auto sdl = make_linkaddr(
+ ":01.02.03.04.05.06.07.08.09.0a.0b.0c.0d.0e.0f."
+ "11.12.13.14.15.16.17.18.19.1a.1b.1c.1d.1e.1f."
+ "22.22.23.24.25.26.27.28.29.2a.2b.2c.2d.2e.2f");
+
+ ATF_REQUIRE_EQ(
+ "1.2.3.4.5.6.7.8.9.a.b.c.d.e.f.11.12.13.14.15.16.17.18.19.1a.1b."s,
+ ::link_ntoa(&sdl));
+}
+
+/*
+ * Test link_ntoa_r, the re-entrant version of link_ntoa().
+ */
+ATF_TEST_CASE_WITHOUT_HEAD(link_ntoa_r)
+ATF_TEST_CASE_BODY(link_ntoa_r)
+{
+ static constexpr char garbage = 0x41;
+ std::vector<char> buf;
+ sockaddr_dl sdl;
+ size_t len;
+ int ret;
+
+ // Return the contents of buf as a string, using the NUL terminator to
+ // determine length. This is to ensure we're using the return value in
+ // the same way C code would, but we do a bit more verification to
+ // elicit a test failure rather than a SEGV if it's broken.
+ auto bufstr = [&buf]() -> std::string_view {
+ // Find the NUL.
+ auto end = std::ranges::find(buf, '\0');
+ ATF_REQUIRE(end != buf.end());
+
+ // Intentionally chopping the NUL off.
+ return {begin(buf), end};
+ };
+
+ // Resize the buffer and set the contents to a known garbage value, so
+ // we don't accidentally have a NUL in the right place when link_ntoa_r
+ // didn't put it there.
+ auto resetbuf = [&buf, &len](std::size_t size) {
+ len = size;
+ buf.resize(len);
+ std::ranges::fill(buf, garbage);
+ };
+
+ // Test a short address with a large buffer.
+ sdl = make_linkaddr("ix0:1.2.3");
+ resetbuf(64);
+ ret = ::link_ntoa_r(&sdl, &buf[0], &len);
+ ATF_REQUIRE_EQ(0, ret);
+ ATF_REQUIRE_EQ(10, len);
+ ATF_REQUIRE_EQ("ix0:1.2.3"s, bufstr());
+
+ // Test a buffer which is exactly the right size.
+ sdl = make_linkaddr("ix0:1.2.3");
+ resetbuf(10);
+ ret = ::link_ntoa_r(&sdl, &buf[0], &len);
+ ATF_REQUIRE_EQ(0, ret);
+ ATF_REQUIRE_EQ(10, len);
+ ATF_REQUIRE_EQ("ix0:1.2.3"sv, bufstr());
+
+ // Test various short buffers, using a table of buffer length and the
+ // output we expect. All of these should produce valid but truncated
+ // strings, with a trailing NUL and with buflen set correctly.
+
+ auto buftests = std::vector<std::pair<std::size_t, std::string_view>>{
+ {1u, ""sv},
+ {2u, ""sv},
+ {3u, ""sv},
+ {4u, "ix0"sv},
+ {5u, "ix0:"sv},
+ {6u, "ix0:1"sv},
+ {7u, "ix0:1."sv},
+ {8u, "ix0:1.2"sv},
+ {9u, "ix0:1.2."sv},
+ };
+
+ for (auto const &[buflen, expected] : buftests) {
+ sdl = make_linkaddr("ix0:1.2.3");
+ resetbuf(buflen);
+ ret = ::link_ntoa_r(&sdl, &buf[0], &len);
+ ATF_REQUIRE_EQ(-1, ret);
+ ATF_REQUIRE_EQ(10, len);
+ ATF_REQUIRE_EQ(expected, bufstr());
+ }
+
+ // Test a NULL buffer, which should just set buflen.
+ sdl = make_linkaddr("ix0:1.2.3");
+ len = 0;
+ ret = ::link_ntoa_r(&sdl, NULL, &len);
+ ATF_REQUIRE_EQ(-1, ret);
+ ATF_REQUIRE_EQ(10, len);
+
+ // A NULL buffer with a non-zero length should also be accepted.
+ sdl = make_linkaddr("ix0:1.2.3");
+ len = 64;
+ ret = ::link_ntoa_r(&sdl, NULL, &len);
+ ATF_REQUIRE_EQ(-1, ret);
+ ATF_REQUIRE_EQ(10, len);
+
+ // Test a non-NULL buffer, but with a length of zero.
+ sdl = make_linkaddr("ix0:1.2.3");
+ resetbuf(1);
+ len = 0;
+ ret = ::link_ntoa_r(&sdl, &buf[0], &len);
+ ATF_REQUIRE_EQ(-1, ret);
+ ATF_REQUIRE_EQ(10, len);
+ // Check we really didn't write anything.
+ ATF_REQUIRE_EQ(garbage, buf[0]);
+
+ // Test a buffer which would be truncated in the middle of a two-digit
+ // hex octet, which should not write the truncated octet at all.
+ sdl = make_linkaddr("ix0:1.22.3");
+ resetbuf(8);
+ ret = ::link_ntoa_r(&sdl, &buf[0], &len);
+ ATF_REQUIRE_EQ(-1, ret);
+ ATF_REQUIRE_EQ(11, len);
+ ATF_REQUIRE_EQ("ix0:1."sv, bufstr());
+}
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ ATF_ADD_TEST_CASE(tcs, basic);
+ ATF_ADD_TEST_CASE(tcs, ifname);
+ ATF_ADD_TEST_CASE(tcs, smallbuf);
+ ATF_ADD_TEST_CASE(tcs, invalid);
+ ATF_ADD_TEST_CASE(tcs, nonether);
+ ATF_ADD_TEST_CASE(tcs, overlong);
+ ATF_ADD_TEST_CASE(tcs, link_ntoa_r);
+}
diff --git a/lib/libc/tests/net/test-eui64.h b/lib/libc/tests/net/test-eui64.h
new file mode 100644
index 000000000000..8bb5fbb956b7
--- /dev/null
+++ b/lib/libc/tests/net/test-eui64.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2004 The Aerospace Corporation. 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 Aerospace Corporation may not be used to endorse or
+ * promote products derived from this software.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AEROSPACE CORPORATION "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AEROSPACE CORPORATION BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 _TEST_EUI64_H
+#define _TEST_EUI64_H
+
+struct eui64 test_eui64_id = {{0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77}};
+struct eui64 test_eui64_eui48 = {{0x00,0x11,0x22,0xFF,0xFE,0x33,0x44,0x55}};
+struct eui64 test_eui64_mac48 = {{0x00,0x11,0x22,0xFF,0xFF,0x33,0x44,0x55}};
+
+#define test_eui64_id_ascii "00-11-22-33-44-55-66-77"
+#define test_eui64_id_colon_ascii "00:11:22:33:44:55:66:77"
+#define test_eui64_hex_ascii "0x0011223344556677"
+#define test_eui64_eui48_ascii "00-11-22-ff-fe-33-44-55"
+#define test_eui64_mac48_ascii "00-11-22-ff-fe-33-44-55"
+#define test_eui64_mac_ascii "00-11-22-33-44-55"
+#define test_eui64_mac_colon_ascii "00:11:22:33:44:55"
+#define test_eui64_id_host "id"
+#define test_eui64_eui48_host "eui-48"
+#define test_eui64_mac48_host "mac-48"
+
+#define test_eui64_line_id "00-11-22-33-44-55-66-77 id"
+#define test_eui64_line_id_colon "00:11:22:33:44:55:66:77 id"
+#define test_eui64_line_eui48 "00-11-22-FF-fe-33-44-55 eui-48"
+#define test_eui64_line_mac48 "00-11-22-FF-ff-33-44-55 mac-48"
+#define test_eui64_line_eui48_6byte "00-11-22-33-44-55 eui-48"
+#define test_eui64_line_eui48_6byte_c "00:11:22:33:44:55 eui-48"
+
+#endif /* !_TEST_EUI64_H */
diff --git a/lib/libc/tests/nss/Makefile b/lib/libc/tests/nss/Makefile
new file mode 100644
index 000000000000..790af8c6312c
--- /dev/null
+++ b/lib/libc/tests/nss/Makefile
@@ -0,0 +1,25 @@
+.PATH: ${.CURDIR:H}/resolv
+
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/lib/libc/nss
+
+BINDIR= ${TESTSDIR}
+
+ATF_TESTS_C+= getaddrinfo_test
+ATF_TESTS_C+= getgr_test
+ATF_TESTS_C+= gethostby_test
+TEST_METADATA.gethostby_test= timeout="1200"
+ATF_TESTS_C+= getpw_test
+ATF_TESTS_C+= getproto_test
+ATF_TESTS_C+= getrpc_test
+ATF_TESTS_C+= getserv_test
+ATF_TESTS_C+= getusershell_test
+
+${PACKAGE}FILES+= mach
+
+WARNS?= 3
+
+CFLAGS+= -I${SRCTOP}/tests
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/nss/Makefile.depend b/lib/libc/tests/nss/Makefile.depend
new file mode 100644
index 000000000000..f17dae18048f
--- /dev/null
+++ b/lib/libc/tests/nss/Makefile.depend
@@ -0,0 +1,19 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/arpa \
+ include/rpc \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/nss/getaddrinfo_test.c b/lib/libc/tests/nss/getaddrinfo_test.c
new file mode 100644
index 000000000000..4528e272a46f
--- /dev/null
+++ b/lib/libc/tests/nss/getaddrinfo_test.c
@@ -0,0 +1,554 @@
+/*-
+ * Copyright (c) 2006 Michael Bushkov <bushman@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stringlist.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "freebsd_test_suite/macros.h"
+#include "testutil.h"
+
+enum test_methods {
+ TEST_GETADDRINFO,
+ TEST_BUILD_SNAPSHOT
+};
+
+static struct addrinfo hints;
+static enum test_methods method = TEST_GETADDRINFO;
+
+DECLARE_TEST_DATA(addrinfo)
+DECLARE_TEST_FILE_SNAPSHOT(addrinfo)
+DECLARE_2PASS_TEST(addrinfo)
+
+static void clone_addrinfo(struct addrinfo *, struct addrinfo const *);
+static int compare_addrinfo(struct addrinfo *, struct addrinfo *, void *);
+static void dump_addrinfo(struct addrinfo *);
+
+static void sdump_addrinfo(struct addrinfo *, char *, size_t);
+
+IMPLEMENT_TEST_DATA(addrinfo)
+IMPLEMENT_TEST_FILE_SNAPSHOT(addrinfo)
+IMPLEMENT_2PASS_TEST(addrinfo)
+
+static void
+clone_addrinfo(struct addrinfo *dest, struct addrinfo const *src)
+{
+
+ ATF_REQUIRE(dest != NULL);
+ ATF_REQUIRE(src != NULL);
+
+ memcpy(dest, src, sizeof(struct addrinfo));
+ if (src->ai_canonname != NULL)
+ dest->ai_canonname = strdup(src->ai_canonname);
+
+ if (src->ai_addr != NULL) {
+ dest->ai_addr = malloc(src->ai_addrlen);
+ ATF_REQUIRE(dest->ai_addr != NULL);
+ memcpy(dest->ai_addr, src->ai_addr, src->ai_addrlen);
+ }
+
+ if (src->ai_next != NULL) {
+ dest->ai_next = malloc(sizeof(struct addrinfo));
+ ATF_REQUIRE(dest->ai_next != NULL);
+ clone_addrinfo(dest->ai_next, src->ai_next);
+ }
+}
+
+static int
+compare_addrinfo_(struct addrinfo *ai1, struct addrinfo *ai2)
+{
+
+ if ((ai1 == NULL) || (ai2 == NULL))
+ return (-1);
+
+ if (ai1->ai_flags != ai2->ai_flags ||
+ ai1->ai_family != ai2->ai_family ||
+ ai1->ai_socktype != ai2->ai_socktype ||
+ ai1->ai_protocol != ai2->ai_protocol ||
+ ai1->ai_addrlen != ai2->ai_addrlen ||
+ ((ai1->ai_addr == NULL || ai2->ai_addr == NULL) &&
+ ai1->ai_addr != ai2->ai_addr) ||
+ ((ai1->ai_canonname == NULL || ai2->ai_canonname == NULL) &&
+ ai1->ai_canonname != ai2->ai_canonname))
+ return (-1);
+
+ if (ai1->ai_canonname != NULL &&
+ strcmp(ai1->ai_canonname, ai2->ai_canonname) != 0)
+ return (-1);
+
+ if (ai1->ai_addr != NULL &&
+ memcmp(ai1->ai_addr, ai2->ai_addr, ai1->ai_addrlen) != 0)
+ return (-1);
+
+ if (ai1->ai_next == NULL && ai2->ai_next == NULL)
+ return (0);
+ else
+ return (compare_addrinfo_(ai1->ai_next, ai2->ai_next));
+}
+
+static int
+compare_addrinfo(struct addrinfo *ai1, struct addrinfo *ai2,
+ void *mdata __unused)
+{
+ int rv;
+
+ printf("testing equality of 2 addrinfo structures\n");
+
+ rv = compare_addrinfo_(ai1, ai2);
+
+ if (rv == 0)
+ printf("equal\n");
+ else {
+ dump_addrinfo(ai1);
+ dump_addrinfo(ai2);
+ printf("not equal\n");
+ }
+
+ return (rv);
+}
+
+static void
+free_addrinfo(struct addrinfo *ai)
+{
+ if (ai == NULL)
+ return;
+
+ free(ai->ai_addr);
+ free(ai->ai_canonname);
+ free_addrinfo(ai->ai_next);
+}
+
+void
+sdump_addrinfo(struct addrinfo *ai, char *buffer, size_t buflen)
+{
+ int written, i;
+
+ written = snprintf(buffer, buflen, "%d %d %d %d %d ",
+ ai->ai_flags, ai->ai_family, ai->ai_socktype, ai->ai_protocol,
+ ai->ai_addrlen);
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+
+ written = snprintf(buffer, buflen, "%s ",
+ ai->ai_canonname == NULL ? "(null)" : ai->ai_canonname);
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+
+ if (ai->ai_addr == NULL) {
+ written = snprintf(buffer, buflen, "(null)");
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+ } else {
+ for (i = 0; i < (int)ai->ai_addrlen; i++) {
+ written = snprintf(buffer, buflen,
+ i + 1 != (int)ai->ai_addrlen ? "%d." : "%d",
+ ((unsigned char *)ai->ai_addr)[i]);
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+
+ if (buflen == 0)
+ return;
+ }
+ }
+
+ if (ai->ai_next != NULL) {
+ written = snprintf(buffer, buflen, ":");
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+
+ sdump_addrinfo(ai->ai_next, buffer, buflen);
+ }
+}
+
+void
+dump_addrinfo(struct addrinfo *result)
+{
+ if (result != NULL) {
+ char buffer[2048];
+ sdump_addrinfo(result, buffer, sizeof(buffer));
+ printf("%s\n", buffer);
+ } else
+ printf("(null)\n");
+}
+
+static int
+addrinfo_read_snapshot_addr(char *addr, unsigned char *result, size_t len)
+{
+ char *s, *ps, *ts;
+
+ ps = addr;
+ while ((s = strsep(&ps, ".")) != NULL) {
+ if (len == 0)
+ return (-1);
+
+ *result = (unsigned char)strtol(s, &ts, 10);
+ ++result;
+ if (*ts != '\0')
+ return (-1);
+
+ --len;
+ }
+ if (len != 0)
+ return (-1);
+ else
+ return (0);
+}
+
+static int
+addrinfo_read_snapshot_ai(struct addrinfo *ai, char *line)
+{
+ char *s, *ps, *ts;
+ int i, rv, *pi;
+
+ rv = 0;
+ i = 0;
+ ps = line;
+ memset(ai, 0, sizeof(struct addrinfo));
+ while ((s = strsep(&ps, " ")) != NULL) {
+ switch (i) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ pi = &ai->ai_flags + i;
+ *pi = (int)strtol(s, &ts, 10);
+ if (*ts != '\0')
+ goto fin;
+ break;
+ case 4:
+ ai->ai_addrlen = (socklen_t)strtol(s, &ts, 10);
+ if (*ts != '\0')
+ goto fin;
+ break;
+ case 5:
+ if (strcmp(s, "(null)") != 0) {
+ ai->ai_canonname = strdup(s);
+ ATF_REQUIRE(ai->ai_canonname != NULL);
+ }
+ break;
+ case 6:
+ if (strcmp(s, "(null)") != 0) {
+ ai->ai_addr = calloc(1, ai->ai_addrlen);
+ ATF_REQUIRE(ai->ai_addr != NULL);
+ rv = addrinfo_read_snapshot_addr(s,
+ (unsigned char *)ai->ai_addr,
+ ai->ai_addrlen);
+
+ if (rv != 0)
+ goto fin;
+ }
+ break;
+ default:
+ /* NOTE: should not be reachable */
+ rv = -1;
+ goto fin;
+ }
+
+ ++i;
+ }
+
+fin:
+ if (i != 7 || rv != 0) {
+ free_addrinfo(ai);
+ ai = NULL;
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+addrinfo_read_snapshot_func(struct addrinfo *ai, char *line)
+{
+ struct addrinfo *ai2;
+ char *s, *ps;
+ int rv;
+
+ printf("1 line read from snapshot:\n%s\n", line);
+
+ rv = 0;
+ ps = line;
+
+ s = strsep(&ps, ":");
+ if (s == NULL)
+ return (-1);
+
+ rv = addrinfo_read_snapshot_ai(ai, s);
+ if (rv != 0)
+ return (-1);
+
+ ai2 = ai;
+ while ((s = strsep(&ps, ":")) != NULL) {
+ ai2->ai_next = calloc(1, sizeof(struct addrinfo));
+ ATF_REQUIRE(ai2->ai_next != NULL);
+
+ rv = addrinfo_read_snapshot_ai(ai2->ai_next, s);
+ if (rv != 0) {
+ free_addrinfo(ai);
+ ai = NULL;
+ return (-1);
+ }
+
+ ai2 = ai2->ai_next;
+ }
+
+ return (0);
+}
+
+static int
+addrinfo_test_correctness(struct addrinfo *ai, void *mdata __unused)
+{
+
+ printf("testing correctness with the following data:\n");
+ dump_addrinfo(ai);
+
+ if (ai == NULL)
+ goto errfin;
+
+ if (!(ai->ai_family >= 0 && ai->ai_family < AF_MAX))
+ goto errfin;
+
+ if (ai->ai_socktype != 0 && ai->ai_socktype != SOCK_STREAM &&
+ ai->ai_socktype != SOCK_DGRAM && ai->ai_socktype != SOCK_RAW)
+ goto errfin;
+
+ if (ai->ai_protocol != 0 && ai->ai_protocol != IPPROTO_UDP &&
+ ai->ai_protocol != IPPROTO_TCP)
+ goto errfin;
+
+ if ((ai->ai_flags & ~(AI_CANONNAME | AI_NUMERICHOST | AI_PASSIVE)) != 0)
+ goto errfin;
+
+ if (ai->ai_addrlen != ai->ai_addr->sa_len ||
+ ai->ai_family != ai->ai_addr->sa_family)
+ goto errfin;
+
+ printf("correct\n");
+
+ return (0);
+errfin:
+ printf("incorrect\n");
+
+ return (-1);
+}
+
+static int
+addrinfo_read_hostlist_func(struct addrinfo *ai, char *line)
+{
+ struct addrinfo *result;
+ int rv;
+
+ printf("resolving %s: ", line);
+ rv = getaddrinfo(line, NULL, &hints, &result);
+ if (rv == 0) {
+ printf("found\n");
+
+ rv = addrinfo_test_correctness(result, NULL);
+ if (rv != 0) {
+ freeaddrinfo(result);
+ result = NULL;
+ return (rv);
+ }
+
+ clone_addrinfo(ai, result);
+ freeaddrinfo(result);
+ result = NULL;
+ } else {
+ printf("not found\n");
+
+ memset(ai, 0, sizeof(struct addrinfo));
+ }
+ return (0);
+}
+
+static void
+run_tests(char *hostlist_file, const char *snapshot_file, int ai_family)
+{
+ struct addrinfo_test_data td, td_snap;
+ char *snapshot_file_copy;
+ int rv;
+
+ if (snapshot_file == NULL)
+ snapshot_file_copy = NULL;
+ else {
+ snapshot_file_copy = strdup(snapshot_file);
+ ATF_REQUIRE(snapshot_file_copy != NULL);
+ }
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = ai_family;
+ hints.ai_flags = AI_CANONNAME;
+
+ if (snapshot_file != NULL)
+ method = TEST_BUILD_SNAPSHOT;
+
+ TEST_DATA_INIT(addrinfo, &td, clone_addrinfo, free_addrinfo);
+ TEST_DATA_INIT(addrinfo, &td_snap, clone_addrinfo, free_addrinfo);
+
+ ATF_REQUIRE_MSG(access(hostlist_file, R_OK) == 0,
+ "can't access the hostlist file %s\n", hostlist_file);
+
+ printf("building host lists from %s\n", hostlist_file);
+
+ rv = TEST_SNAPSHOT_FILE_READ(addrinfo, hostlist_file, &td,
+ addrinfo_read_hostlist_func);
+ if (rv != 0)
+ goto fin;
+
+ if (snapshot_file != NULL) {
+ if (access(snapshot_file, W_OK | R_OK) != 0) {
+ if (errno == ENOENT)
+ method = TEST_BUILD_SNAPSHOT;
+ else {
+ printf("can't access the snapshot "
+ "file %s\n", snapshot_file);
+
+ rv = -1;
+ goto fin;
+ }
+ } else {
+ rv = TEST_SNAPSHOT_FILE_READ(addrinfo, snapshot_file,
+ &td_snap, addrinfo_read_snapshot_func);
+ if (rv != 0) {
+ printf("error reading snapshot file: %s\n",
+ strerror(errno));
+ goto fin;
+ }
+ }
+ }
+
+ switch (method) {
+ case TEST_GETADDRINFO:
+ if (snapshot_file != NULL)
+ ATF_CHECK(DO_2PASS_TEST(addrinfo, &td, &td_snap,
+ compare_addrinfo, NULL) == 0);
+ break;
+ case TEST_BUILD_SNAPSHOT:
+ if (snapshot_file != NULL) {
+ ATF_CHECK(TEST_SNAPSHOT_FILE_WRITE(addrinfo,
+ snapshot_file, &td, sdump_addrinfo) == 0);
+ }
+ break;
+ default:
+ break;
+ }
+
+fin:
+ TEST_DATA_DESTROY(addrinfo, &td_snap);
+ TEST_DATA_DESTROY(addrinfo, &td);
+
+ free(snapshot_file_copy);
+}
+
+#define HOSTLIST_FILE "mach"
+#define RUN_TESTS(tc, snapshot_file, ai_family) do { \
+ char *_hostlist_file; \
+ ATF_REQUIRE(0 < asprintf(&_hostlist_file, "%s/%s", \
+ atf_tc_get_config_var(tc, "srcdir"), HOSTLIST_FILE)); \
+ run_tests(_hostlist_file, snapshot_file, ai_family); \
+ free(_hostlist_file); \
+} while (0)
+
+ATF_TC_WITHOUT_HEAD(pf_unspec);
+ATF_TC_BODY(pf_unspec, tc)
+{
+
+ RUN_TESTS(tc, NULL, AF_UNSPEC);
+}
+
+ATF_TC_WITHOUT_HEAD(pf_unspec_with_snapshot);
+ATF_TC_BODY(pf_unspec_with_snapshot, tc)
+{
+
+ RUN_TESTS(tc, "snapshot_ai", AF_UNSPEC);
+}
+
+ATF_TC_WITHOUT_HEAD(pf_inet);
+ATF_TC_BODY(pf_inet, tc)
+{
+
+ ATF_REQUIRE_FEATURE("inet");
+ RUN_TESTS(tc, NULL, AF_INET);
+}
+
+ATF_TC_WITHOUT_HEAD(pf_inet_with_snapshot);
+ATF_TC_BODY(pf_inet_with_snapshot, tc)
+{
+
+ ATF_REQUIRE_FEATURE("inet");
+ RUN_TESTS(tc, "snapshot_ai4", AF_INET);
+}
+
+ATF_TC_WITHOUT_HEAD(pf_inet6);
+ATF_TC_BODY(pf_inet6, tc)
+{
+
+ ATF_REQUIRE_FEATURE("inet6");
+ RUN_TESTS(tc, NULL, AF_INET6);
+}
+
+ATF_TC_WITHOUT_HEAD(pf_inet6_with_snapshot);
+ATF_TC_BODY(pf_inet6_with_snapshot, tc)
+{
+
+ ATF_REQUIRE_FEATURE("inet6");
+ RUN_TESTS(tc, "snapshot_ai6", AF_INET6);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, pf_unspec);
+ ATF_TP_ADD_TC(tp, pf_unspec_with_snapshot);
+ ATF_TP_ADD_TC(tp, pf_inet);
+ ATF_TP_ADD_TC(tp, pf_inet_with_snapshot);
+ ATF_TP_ADD_TC(tp, pf_inet6);
+ ATF_TP_ADD_TC(tp, pf_inet6_with_snapshot);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/nss/getgr_test.c b/lib/libc/tests/nss/getgr_test.c
new file mode 100644
index 000000000000..974632d4b7c7
--- /dev/null
+++ b/lib/libc/tests/nss/getgr_test.c
@@ -0,0 +1,574 @@
+/*-
+ * Copyright (c) 2006 Michael Bushkov <bushman@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <grp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stringlist.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "testutil.h"
+
+enum test_methods {
+ TEST_GETGRENT,
+ TEST_GETGRNAM,
+ TEST_GETGRGID,
+ TEST_GETGRENT_2PASS,
+ TEST_GETGRENT_INTERLEAVED_GETGRNAM,
+ TEST_GETGRENT_INTERLEAVED_GETGRGID,
+ TEST_BUILD_SNAPSHOT,
+};
+
+DECLARE_TEST_DATA(group)
+DECLARE_TEST_FILE_SNAPSHOT(group)
+DECLARE_1PASS_TEST(group)
+DECLARE_2PASS_TEST(group)
+
+static void clone_group(struct group *, struct group const *);
+static int compare_group(struct group *, struct group *, void *);
+static void dump_group(struct group *);
+static void free_group(struct group *);
+
+static void sdump_group(struct group *, char *, size_t);
+static int group_read_snapshot_func(struct group *, char *);
+
+static int group_check_ambiguity(struct group_test_data *, struct group *);
+static int group_fill_test_data(struct group_test_data *,
+ int (*cb)(struct group *, void *));
+static int group_test_correctness(struct group *, void *);
+static int group_test_getgrnam(struct group *, void *);
+static int group_test_getgrgid(struct group *, void *);
+static int group_test_getgrent(struct group *, void *);
+
+IMPLEMENT_TEST_DATA(group)
+IMPLEMENT_TEST_FILE_SNAPSHOT(group)
+IMPLEMENT_1PASS_TEST(group)
+IMPLEMENT_2PASS_TEST(group)
+
+static void
+clone_group(struct group *dest, struct group const *src)
+{
+ ATF_REQUIRE(dest != NULL);
+ ATF_REQUIRE(src != NULL);
+
+ char **cp;
+ int members_num;
+
+ memset(dest, 0, sizeof(struct group));
+
+ if (src->gr_name != NULL) {
+ dest->gr_name = strdup(src->gr_name);
+ ATF_REQUIRE(dest->gr_name != NULL);
+ }
+
+ if (src->gr_passwd != NULL) {
+ dest->gr_passwd = strdup(src->gr_passwd);
+ ATF_REQUIRE(dest->gr_passwd != NULL);
+ }
+ dest->gr_gid = src->gr_gid;
+
+ if (src->gr_mem != NULL) {
+ members_num = 0;
+ for (cp = src->gr_mem; *cp; ++cp)
+ ++members_num;
+
+ dest->gr_mem = calloc(members_num + 1, sizeof(char *));
+ ATF_REQUIRE(dest->gr_mem != NULL);
+
+ for (cp = src->gr_mem; *cp; ++cp) {
+ dest->gr_mem[cp - src->gr_mem] = strdup(*cp);
+ ATF_REQUIRE(dest->gr_mem[cp - src->gr_mem] != NULL);
+ }
+ }
+}
+
+static void
+free_group(struct group *grp)
+{
+ char **cp;
+
+ ATF_REQUIRE(grp != NULL);
+
+ free(grp->gr_name);
+ free(grp->gr_passwd);
+
+ for (cp = grp->gr_mem; *cp; ++cp)
+ free(*cp);
+ free(grp->gr_mem);
+}
+
+static int
+compare_group(struct group *grp1, struct group *grp2, void *mdata)
+{
+ char **c1, **c2;
+
+ if (grp1 == grp2)
+ return (0);
+
+ if (grp1 == NULL || grp2 == NULL)
+ goto errfin;
+
+ if (strcmp(grp1->gr_name, grp2->gr_name) != 0 ||
+ strcmp(grp1->gr_passwd, grp2->gr_passwd) != 0 ||
+ grp1->gr_gid != grp2->gr_gid)
+ goto errfin;
+
+ c1 = grp1->gr_mem;
+ c2 = grp2->gr_mem;
+
+ if (grp1->gr_mem == NULL || grp2->gr_mem == NULL)
+ goto errfin;
+
+ for (; *c1 && *c2; ++c1, ++c2)
+ if (strcmp(*c1, *c2) != 0)
+ goto errfin;
+
+ if (*c1 != NULL || *c2 != NULL)
+ goto errfin;
+
+ return 0;
+
+errfin:
+ if (mdata == NULL) {
+ printf("following structures are not equal:\n");
+ dump_group(grp1);
+ dump_group(grp2);
+ }
+
+ return (-1);
+}
+
+static void
+sdump_group(struct group *grp, char *buffer, size_t buflen)
+{
+ char **cp;
+ int written;
+
+ written = snprintf(buffer, buflen, "%s:%s:%d:",
+ grp->gr_name, grp->gr_passwd, grp->gr_gid);
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+
+ if (grp->gr_mem != NULL) {
+ if (*(grp->gr_mem) != NULL) {
+ for (cp = grp->gr_mem; *cp; ++cp) {
+ written = snprintf(buffer, buflen, "%s%s",
+ cp == grp->gr_mem ? "" : ",", *cp);
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+
+ if (buflen == 0)
+ return;
+ }
+ } else
+ snprintf(buffer, buflen, "nomem");
+ } else
+ snprintf(buffer, buflen, "(null)");
+}
+
+static int
+group_read_snapshot_func(struct group *grp, char *line)
+{
+ StringList *sl;
+ char *s, *ps, *ts;
+ const char *sep;
+ int i;
+
+ printf("1 line read from snapshot:\n%s\n", line);
+
+ i = 0;
+ sl = NULL;
+ ps = line;
+ sep = ":";
+ memset(grp, 0, sizeof(struct group));
+ while ((s = strsep(&ps, sep)) != NULL) {
+ switch (i) {
+ case 0:
+ grp->gr_name = strdup(s);
+ ATF_REQUIRE(grp->gr_name != NULL);
+ break;
+
+ case 1:
+ grp->gr_passwd = strdup(s);
+ ATF_REQUIRE(grp->gr_passwd != NULL);
+ break;
+
+ case 2:
+ grp->gr_gid = (gid_t)strtol(s, &ts, 10);
+ if (*ts != '\0') {
+ free(grp->gr_name);
+ free(grp->gr_passwd);
+ grp->gr_name = NULL;
+ grp->gr_passwd = NULL;
+ return (-1);
+ }
+ /* Change to parsing groups. */
+ sep = ",";
+ break;
+
+ default:
+ if (sl == NULL) {
+ if (strcmp(s, "(null)") == 0)
+ return (0);
+
+ sl = sl_init();
+ ATF_REQUIRE(sl != NULL);
+
+ if (strcmp(s, "nomem") != 0) {
+ ts = strdup(s);
+ ATF_REQUIRE(ts != NULL);
+ sl_add(sl, ts);
+ }
+ } else {
+ ts = strdup(s);
+ ATF_REQUIRE(ts != NULL);
+ sl_add(sl, ts);
+ }
+ break;
+ }
+ ++i;
+ }
+
+ if (i < 3) {
+ free(grp->gr_name);
+ free(grp->gr_passwd);
+ memset(grp, 0, sizeof(struct group));
+ return (-1);
+ }
+
+ sl_add(sl, NULL);
+ grp->gr_mem = sl->sl_str;
+
+ /* NOTE: is it a dirty hack or not? */
+ free(sl);
+ return (0);
+}
+
+static void
+dump_group(struct group *result)
+{
+ if (result != NULL) {
+ char buffer[1024];
+ sdump_group(result, buffer, sizeof(buffer));
+ printf("%s\n", buffer);
+ } else
+ printf("(null)\n");
+}
+
+static int
+group_fill_test_data(struct group_test_data *td,
+ int (*cb)(struct group *, void *))
+{
+ struct group *grp;
+ const int limit = 1024;
+ int count = 0;
+
+ setgroupent(1);
+ while ((grp = getgrent()) != NULL) {
+ if (group_test_correctness(grp, NULL) == 0) {
+ TEST_DATA_APPEND(group, td, grp);
+ if (cb != NULL && cb(grp, td) != 0)
+ return (-1);
+ } else {
+ return (-1);
+ }
+ if (++count >= limit)
+ break;
+ }
+ endgrent();
+
+ return (0);
+}
+
+static int
+group_test_correctness(struct group *grp, void *mdata __unused)
+{
+ printf("testing correctness with the following data:\n");
+ dump_group(grp);
+
+ if (grp == NULL)
+ goto errfin;
+
+ if (grp->gr_name == NULL)
+ goto errfin;
+
+ if (grp->gr_passwd == NULL)
+ goto errfin;
+
+ if (grp->gr_mem == NULL)
+ goto errfin;
+
+ printf("correct\n");
+
+ return (0);
+errfin:
+ printf("incorrect\n");
+
+ return (-1);
+}
+
+/* group_check_ambiguity() is needed here because when doing the getgrent()
+ * calls sequence, records from different nsswitch sources can be different,
+ * though having the same pw_name/pw_uid */
+static int
+group_check_ambiguity(struct group_test_data *td, struct group *pwd)
+{
+
+ return (TEST_DATA_FIND(group, td, pwd, compare_group, NULL) !=
+ NULL ? 0 : -1);
+}
+
+static int
+group_test_getgrnam(struct group *grp_model, void *mdata)
+{
+ struct group *grp;
+
+ printf("testing getgrnam() with the following data:\n");
+ dump_group(grp_model);
+
+ grp = getgrnam(grp_model->gr_name);
+ if (group_test_correctness(grp, NULL) != 0)
+ goto errfin;
+
+ if (compare_group(grp, grp_model, NULL) != 0 &&
+ group_check_ambiguity((struct group_test_data *)mdata, grp) != 0)
+ goto errfin;
+
+ return (0);
+
+errfin:
+ return (-1);
+}
+
+static int
+group_test_getgrgid(struct group *grp_model, void *mdata)
+{
+ struct group *grp;
+
+ printf("testing getgrgid() with the following data...\n");
+ dump_group(grp_model);
+
+ grp = getgrgid(grp_model->gr_gid);
+ if (group_test_correctness(grp, NULL) != 0 ||
+ (compare_group(grp, grp_model, NULL) != 0 &&
+ group_check_ambiguity((struct group_test_data *)mdata, grp) != 0))
+ return (-1);
+ else
+ return (0);
+}
+
+static int
+group_test_getgrent(struct group *grp, void *mdata __unused)
+{
+ /*
+ * Only correctness can be checked when doing 1-pass test for
+ * getgrent().
+ */
+ return (group_test_correctness(grp, NULL));
+}
+
+static int
+run_tests(const char *snapshot_file, enum test_methods method)
+{
+ struct group_test_data td, td_snap, td_2pass, td_interleaved;
+ int rv;
+
+ TEST_DATA_INIT(group, &td, clone_group, free_group);
+ TEST_DATA_INIT(group, &td_snap, clone_group, free_group);
+ if (snapshot_file != NULL) {
+ if (access(snapshot_file, W_OK | R_OK) != 0) {
+ if (errno == ENOENT)
+ method = TEST_BUILD_SNAPSHOT;
+ else {
+ printf("can't access the file %s\n",
+ snapshot_file);
+
+ rv = -1;
+ goto fin;
+ }
+ } else {
+ if (method == TEST_BUILD_SNAPSHOT) {
+ rv = 0;
+ goto fin;
+ }
+
+ TEST_SNAPSHOT_FILE_READ(group, snapshot_file,
+ &td_snap, group_read_snapshot_func);
+ }
+ }
+
+ rv = group_fill_test_data(&td, NULL);
+ if (rv == -1)
+ return (-1);
+ switch (method) {
+ case TEST_GETGRNAM:
+ if (snapshot_file == NULL)
+ rv = DO_1PASS_TEST(group, &td,
+ group_test_getgrnam, (void *)&td);
+ else
+ rv = DO_1PASS_TEST(group, &td_snap,
+ group_test_getgrnam, (void *)&td_snap);
+ break;
+ case TEST_GETGRGID:
+ if (snapshot_file == NULL)
+ rv = DO_1PASS_TEST(group, &td,
+ group_test_getgrgid, (void *)&td);
+ else
+ rv = DO_1PASS_TEST(group, &td_snap,
+ group_test_getgrgid, (void *)&td_snap);
+ break;
+ case TEST_GETGRENT:
+ if (snapshot_file == NULL)
+ rv = DO_1PASS_TEST(group, &td, group_test_getgrent,
+ (void *)&td);
+ else
+ rv = DO_2PASS_TEST(group, &td, &td_snap,
+ compare_group, NULL);
+ break;
+ case TEST_GETGRENT_2PASS:
+ TEST_DATA_INIT(group, &td_2pass, clone_group, free_group);
+ rv = group_fill_test_data(&td_2pass, NULL);
+ if (rv != -1)
+ rv = DO_2PASS_TEST(group, &td, &td_2pass,
+ compare_group, NULL);
+ TEST_DATA_DESTROY(group, &td_2pass);
+ break;
+ case TEST_GETGRENT_INTERLEAVED_GETGRNAM:
+ TEST_DATA_INIT(group, &td_interleaved, clone_group, free_group);
+ rv = group_fill_test_data(&td_interleaved, group_test_getgrnam);
+ if (rv != -1)
+ rv = DO_2PASS_TEST(group, &td, &td_interleaved,
+ compare_group, NULL);
+ TEST_DATA_DESTROY(group, &td_interleaved);
+ break;
+ case TEST_GETGRENT_INTERLEAVED_GETGRGID:
+ TEST_DATA_INIT(group, &td_interleaved, clone_group, free_group);
+ rv = group_fill_test_data(&td_interleaved, group_test_getgrgid);
+ if (rv != -1)
+ rv = DO_2PASS_TEST(group, &td, &td_interleaved,
+ compare_group, NULL);
+ TEST_DATA_DESTROY(group, &td_interleaved);
+ break;
+ case TEST_BUILD_SNAPSHOT:
+ if (snapshot_file != NULL)
+ rv = TEST_SNAPSHOT_FILE_WRITE(group, snapshot_file, &td,
+ sdump_group);
+ break;
+ default:
+ rv = 0;
+ break;
+ }
+
+fin:
+ TEST_DATA_DESTROY(group, &td_snap);
+ TEST_DATA_DESTROY(group, &td);
+
+ return (rv);
+}
+
+#define SNAPSHOT_FILE "snapshot_grp"
+
+ATF_TC_WITHOUT_HEAD(getgrent);
+ATF_TC_BODY(getgrent, tc)
+{
+ ATF_REQUIRE(run_tests(NULL, TEST_GETGRENT) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getgrent_with_snapshot);
+ATF_TC_BODY(getgrent_with_snapshot, tc)
+{
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETGRENT) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getgrent_with_two_pass);
+ATF_TC_BODY(getgrent_with_two_pass, tc)
+{
+ ATF_REQUIRE(run_tests(NULL, TEST_GETGRENT_2PASS) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getgrgid);
+ATF_TC_BODY(getgrgid, tc)
+{
+ ATF_REQUIRE(run_tests(NULL, TEST_GETGRGID) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getgrgid_with_snapshot);
+ATF_TC_BODY(getgrgid_with_snapshot, tc)
+{
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETGRGID) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getgrnam);
+ATF_TC_BODY(getgrnam, tc)
+{
+ ATF_REQUIRE(run_tests(NULL, TEST_GETGRNAM) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getgrnam_with_snapshot);
+ATF_TC_BODY(getgrnam_with_snapshot, tc)
+{
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETGRNAM) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getgrent_interleaved_getgrnam);
+ATF_TC_BODY(getgrent_interleaved_getgrnam, tc)
+{
+ ATF_REQUIRE(run_tests(NULL, TEST_GETGRENT_INTERLEAVED_GETGRNAM) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getgrent_interleaved_getgrgid);
+ATF_TC_BODY(getgrent_interleaved_getgrgid, tc)
+{
+ ATF_REQUIRE(run_tests(NULL, TEST_GETGRENT_INTERLEAVED_GETGRGID) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, getgrent);
+ ATF_TP_ADD_TC(tp, getgrent_with_snapshot);
+ ATF_TP_ADD_TC(tp, getgrent_with_two_pass);
+ ATF_TP_ADD_TC(tp, getgrgid);
+ ATF_TP_ADD_TC(tp, getgrgid_with_snapshot);
+ ATF_TP_ADD_TC(tp, getgrnam);
+ ATF_TP_ADD_TC(tp, getgrnam_with_snapshot);
+ ATF_TP_ADD_TC(tp, getgrent_interleaved_getgrnam);
+ ATF_TP_ADD_TC(tp, getgrent_interleaved_getgrgid);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/nss/gethostby_test.c b/lib/libc/tests/nss/gethostby_test.c
new file mode 100644
index 000000000000..0ed96170fc6d
--- /dev/null
+++ b/lib/libc/tests/nss/gethostby_test.c
@@ -0,0 +1,1510 @@
+/*-
+ * Copyright (c) 2006 Michael Bushkov <bushman@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <errno.h>
+#include <netdb.h>
+#include <resolv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stringlist.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "freebsd_test_suite/macros.h"
+#include "testutil.h"
+
+enum test_methods {
+ TEST_GETHOSTBYNAME2,
+ TEST_GETHOSTBYADDR,
+ TEST_GETHOSTBYNAME2_GETADDRINFO,
+ TEST_GETHOSTBYADDR_GETNAMEINFO,
+ TEST_BUILD_SNAPSHOT,
+ TEST_BUILD_ADDR_SNAPSHOT
+};
+
+static int ipnode_flags = 0;
+static int af_type = AF_INET;
+static bool use_ipnode_functions;
+
+DECLARE_TEST_DATA(hostent)
+DECLARE_TEST_FILE_SNAPSHOT(hostent)
+DECLARE_1PASS_TEST(hostent)
+DECLARE_2PASS_TEST(hostent)
+
+/* These stubs will use gethostby***() or getipnodeby***() functions,
+ * depending on the use_ipnode_functions global variable value */
+static struct hostent *__gethostbyname2(const char *, int);
+static struct hostent *__gethostbyaddr(const void *, socklen_t, int);
+static void __freehostent(struct hostent *);
+
+static void clone_hostent(struct hostent *, struct hostent const *);
+static int compare_hostent(struct hostent *, struct hostent *, void *);
+static void dump_hostent(struct hostent *);
+static void free_hostent(struct hostent *);
+
+static int is_hostent_equal(struct hostent *, struct addrinfo *);
+
+static void sdump_hostent(struct hostent *, char *, size_t);
+static int hostent_read_hostlist_func(struct hostent *, char *);
+static int hostent_read_snapshot_addr(char *, unsigned char *, size_t);
+static int hostent_read_snapshot_func(struct hostent *, char *);
+
+static int hostent_test_correctness(struct hostent *, void *);
+static int hostent_test_gethostbyaddr(struct hostent *, void *);
+static int hostent_test_getaddrinfo_eq(struct hostent *, void *);
+static int hostent_test_getnameinfo_eq(struct hostent *, void *);
+
+IMPLEMENT_TEST_DATA(hostent)
+IMPLEMENT_TEST_FILE_SNAPSHOT(hostent)
+IMPLEMENT_1PASS_TEST(hostent)
+IMPLEMENT_2PASS_TEST(hostent)
+
+static struct hostent *
+__gethostbyname2(const char *name, int af)
+{
+ struct hostent *he;
+ int error;
+
+ if (use_ipnode_functions) {
+ error = 0;
+ he = getipnodebyname(name, af, ipnode_flags, &error);
+ if (he == NULL)
+ errno = error;
+ } else
+ he = gethostbyname2(name, af);
+
+ return (he);
+}
+
+static struct hostent *
+__gethostbyaddr(const void *addr, socklen_t len, int af)
+{
+ struct hostent *he;
+ int error;
+
+ if (use_ipnode_functions) {
+ error = 0;
+ he = getipnodebyaddr(addr, len, af, &error);
+ if (he == NULL)
+ errno = error;
+ } else
+ he = gethostbyaddr(addr, len, af);
+
+ return (he);
+}
+
+static void
+__freehostent(struct hostent *he)
+{
+
+ /* NOTE: checking for he != NULL - just in case */
+ if (use_ipnode_functions && he != NULL)
+ freehostent(he);
+}
+
+static void
+clone_hostent(struct hostent *dest, struct hostent const *src)
+{
+ ATF_REQUIRE(dest != NULL);
+ ATF_REQUIRE(src != NULL);
+
+ char **cp;
+ int aliases_num;
+ int addrs_num;
+ size_t offset;
+
+ memset(dest, 0, sizeof(struct hostent));
+
+ if (src->h_name != NULL) {
+ dest->h_name = strdup(src->h_name);
+ ATF_REQUIRE(dest->h_name != NULL);
+ }
+
+ dest->h_addrtype = src->h_addrtype;
+ dest->h_length = src->h_length;
+
+ if (src->h_aliases != NULL) {
+ aliases_num = 0;
+ for (cp = src->h_aliases; *cp; ++cp)
+ ++aliases_num;
+
+ dest->h_aliases = calloc(aliases_num + 1, sizeof(char *));
+ ATF_REQUIRE(dest->h_aliases != NULL);
+
+ for (cp = src->h_aliases; *cp; ++cp) {
+ dest->h_aliases[cp - src->h_aliases] = strdup(*cp);
+ ATF_REQUIRE(dest->h_aliases[cp - src->h_aliases] != NULL);
+ }
+ }
+
+ if (src->h_addr_list != NULL) {
+ addrs_num = 0;
+ for (cp = src->h_addr_list; *cp; ++cp)
+ ++addrs_num;
+
+ dest->h_addr_list = calloc(addrs_num + 1, sizeof(char *));
+ ATF_REQUIRE(dest->h_addr_list != NULL);
+
+ for (cp = src->h_addr_list; *cp; ++cp) {
+ offset = cp - src->h_addr_list;
+ dest->h_addr_list[offset] = malloc(src->h_length);
+ ATF_REQUIRE(dest->h_addr_list[offset] != NULL);
+ memcpy(dest->h_addr_list[offset],
+ src->h_addr_list[offset], src->h_length);
+ }
+ }
+}
+
+static void
+free_hostent(struct hostent *ht)
+{
+ char **cp;
+
+ ATF_REQUIRE(ht != NULL);
+
+ free(ht->h_name);
+
+ if (ht->h_aliases != NULL) {
+ for (cp = ht->h_aliases; *cp; ++cp)
+ free(*cp);
+ free(ht->h_aliases);
+ }
+
+ if (ht->h_addr_list != NULL) {
+ for (cp = ht->h_addr_list; *cp; ++cp)
+ free(*cp);
+ free(ht->h_addr_list);
+ }
+}
+
+static int
+compare_hostent(struct hostent *ht1, struct hostent *ht2, void *mdata)
+{
+ char **c1, **c2, **ct, **cb;
+ int b;
+
+ if (ht1 == ht2)
+ return 0;
+
+ if (ht1 == NULL || ht2 == NULL)
+ goto errfin;
+
+ if (ht1->h_name == NULL || ht2->h_name == NULL)
+ goto errfin;
+
+ if (ht1->h_addrtype != ht2->h_addrtype ||
+ ht1->h_length != ht2->h_length ||
+ strcmp(ht1->h_name, ht2->h_name) != 0)
+ goto errfin;
+
+ c1 = ht1->h_aliases;
+ c2 = ht2->h_aliases;
+
+ if ((ht1->h_aliases == NULL || ht2->h_aliases == NULL) &&
+ ht1->h_aliases != ht2->h_aliases)
+ goto errfin;
+
+ if (c1 != NULL && c2 != NULL) {
+ cb = c1;
+ for (;*c1; ++c1) {
+ b = 0;
+ for (ct = c2; *ct; ++ct) {
+ if (strcmp(*c1, *ct) == 0) {
+ b = 1;
+ break;
+ }
+ }
+ if (b == 0) {
+ printf("h1 aliases item can't be found in h2 "
+ "aliases\n");
+ goto errfin;
+ }
+ }
+
+ c1 = cb;
+ for (;*c2; ++c2) {
+ b = 0;
+ for (ct = c1; *ct; ++ct) {
+ if (strcmp(*c2, *ct) == 0) {
+ b = 1;
+ break;
+ }
+ }
+ if (b == 0) {
+ printf("h2 aliases item can't be found in h1 "
+ "aliases\n");
+ goto errfin;
+ }
+ }
+ }
+
+ c1 = ht1->h_addr_list;
+ c2 = ht2->h_addr_list;
+
+ if ((ht1->h_addr_list == NULL || ht2->h_addr_list== NULL) &&
+ ht1->h_addr_list != ht2->h_addr_list)
+ goto errfin;
+
+ if (c1 != NULL && c2 != NULL) {
+ cb = c1;
+ for (; *c1; ++c1) {
+ b = 0;
+ for (ct = c2; *ct; ++ct) {
+ if (memcmp(*c1, *ct, ht1->h_length) == 0) {
+ b = 1;
+ break;
+ }
+ }
+ if (b == 0) {
+ printf("h1 addresses item can't be found in "
+ "h2 addresses\n");
+ goto errfin;
+ }
+ }
+
+ c1 = cb;
+ for (; *c2; ++c2) {
+ b = 0;
+ for (ct = c1; *ct; ++ct) {
+ if (memcmp(*c2, *ct, ht1->h_length) == 0) {
+ b = 1;
+ break;
+ }
+ }
+ if (b == 0) {
+ printf("h2 addresses item can't be found in "
+ "h1 addresses\n");
+ goto errfin;
+ }
+ }
+ }
+
+ return 0;
+
+errfin:
+ if (mdata == NULL) {
+ printf("following structures are not equal:\n");
+ dump_hostent(ht1);
+ dump_hostent(ht2);
+ }
+
+ return (-1);
+}
+
+static int
+check_addrinfo_for_name(struct addrinfo *ai, char const *name)
+{
+ struct addrinfo *ai2;
+
+ for (ai2 = ai; ai2 != NULL; ai2 = ai2->ai_next) {
+ if (strcmp(ai2->ai_canonname, name) == 0)
+ return (0);
+ }
+
+ return (-1);
+}
+
+static int
+check_addrinfo_for_addr(struct addrinfo *ai, char const *addr,
+ socklen_t addrlen, int af)
+{
+ struct addrinfo *ai2;
+
+ for (ai2 = ai; ai2 != NULL; ai2 = ai2->ai_next) {
+ if (af != ai2->ai_family)
+ continue;
+
+ switch (af) {
+ case AF_INET:
+ if (memcmp(addr,
+ (void *)&((struct sockaddr_in *)ai2->ai_addr)->sin_addr,
+ MIN(addrlen, ai2->ai_addrlen)) == 0)
+ return (0);
+ break;
+ case AF_INET6:
+ if (memcmp(addr,
+ (void *)&((struct sockaddr_in6 *)ai2->ai_addr)->sin6_addr,
+ MIN(addrlen, ai2->ai_addrlen)) == 0)
+ return (0);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return (-1);
+}
+
+static int
+is_hostent_equal(struct hostent *he, struct addrinfo *ai)
+{
+ char **cp;
+ int rv;
+
+#ifdef DEBUG
+ printf("checking equality of he and ai\n");
+#endif
+
+ rv = check_addrinfo_for_name(ai, he->h_name);
+ if (rv != 0) {
+ printf("not equal - he->h_name couldn't be found\n");
+ return (rv);
+ }
+
+ for (cp = he->h_addr_list; *cp; ++cp) {
+ rv = check_addrinfo_for_addr(ai, *cp, he->h_length,
+ he->h_addrtype);
+ if (rv != 0) {
+ printf("not equal - one of he->h_addr_list couldn't be found\n");
+ return (rv);
+ }
+ }
+
+#ifdef DEBUG
+ printf("equal\n");
+#endif
+
+ return (0);
+}
+
+static void
+sdump_hostent(struct hostent *ht, char *buffer, size_t buflen)
+{
+ char **cp;
+ size_t i;
+ int written;
+
+ written = snprintf(buffer, buflen, "%s %d %d",
+ ht->h_name, ht->h_addrtype, ht->h_length);
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+
+ if (ht->h_aliases != NULL) {
+ if (*(ht->h_aliases) != NULL) {
+ for (cp = ht->h_aliases; *cp; ++cp) {
+ written = snprintf(buffer, buflen, " %s",*cp);
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+
+ if (buflen == 0)
+ return;
+ }
+ } else {
+ written = snprintf(buffer, buflen, " noaliases");
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+ }
+ } else {
+ written = snprintf(buffer, buflen, " (null)");
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+ }
+
+ written = snprintf(buffer, buflen, " : ");
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+
+ if (ht->h_addr_list != NULL) {
+ if (*(ht->h_addr_list) != NULL) {
+ for (cp = ht->h_addr_list; *cp; ++cp) {
+ for (i = 0; i < (size_t)ht->h_length; ++i) {
+ written = snprintf(buffer, buflen,
+ i + 1 != (size_t)ht->h_length ?
+ "%d." : "%d",
+ (unsigned char)(*cp)[i]);
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+
+ if (buflen == 0)
+ return;
+ }
+
+ if (*(cp + 1)) {
+ written = snprintf(buffer, buflen,
+ " ");
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+ }
+ }
+ } else {
+ written = snprintf(buffer, buflen, " noaddrs");
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+ }
+ } else {
+ written = snprintf(buffer, buflen, " (null)");
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+ }
+}
+
+static int
+hostent_read_hostlist_func(struct hostent *he, char *line)
+{
+ struct hostent *result;
+ int rv;
+
+#ifdef DEBUG
+ printf("resolving %s: ", line);
+#endif
+ result = __gethostbyname2(line, af_type);
+ if (result != NULL) {
+#ifdef DEBUG
+ printf("found\n");
+#endif
+
+ rv = hostent_test_correctness(result, NULL);
+ if (rv != 0) {
+ __freehostent(result);
+ return (rv);
+ }
+
+ clone_hostent(he, result);
+ __freehostent(result);
+ } else {
+#ifdef DEBUG
+ printf("not found\n");
+#endif
+ memset(he, 0, sizeof(struct hostent));
+ he->h_name = strdup(line);
+ ATF_REQUIRE(he->h_name != NULL);
+ }
+ return (0);
+}
+
+static int
+hostent_read_snapshot_addr(char *addr, unsigned char *result, size_t len)
+{
+ char *s, *ps, *ts;
+
+ ps = addr;
+ while ( (s = strsep(&ps, ".")) != NULL) {
+ if (len == 0)
+ return (-1);
+
+ *result = (unsigned char)strtol(s, &ts, 10);
+ ++result;
+ if (*ts != '\0')
+ return (-1);
+
+ --len;
+ }
+ if (len != 0)
+ return (-1);
+ else
+ return (0);
+}
+
+static int
+hostent_read_snapshot_func(struct hostent *ht, char *line)
+{
+ StringList *sl1, *sl2;
+ char *s, *ps, *ts;
+ int i, rv;
+
+#ifdef DEBUG
+ printf("1 line read from snapshot:\n%s\n", line);
+#endif
+
+ rv = 0;
+ i = 0;
+ sl1 = sl2 = NULL;
+ ps = line;
+ memset(ht, 0, sizeof(struct hostent));
+ while ((s = strsep(&ps, " ")) != NULL) {
+ switch (i) {
+ case 0:
+ ht->h_name = strdup(s);
+ ATF_REQUIRE(ht->h_name != NULL);
+ break;
+
+ case 1:
+ ht->h_addrtype = (int)strtol(s, &ts, 10);
+ if (*ts != '\0')
+ goto fin;
+ break;
+
+ case 2:
+ ht->h_length = (int)strtol(s, &ts, 10);
+ if (*ts != '\0')
+ goto fin;
+ break;
+
+ case 3:
+ if (sl1 == NULL) {
+ if (strcmp(s, "(null)") == 0)
+ return (0);
+
+ sl1 = sl_init();
+ ATF_REQUIRE(sl1 != NULL);
+
+ if (strcmp(s, "noaliases") != 0) {
+ ts = strdup(s);
+ ATF_REQUIRE(ts != NULL);
+ sl_add(sl1, ts);
+ }
+ } else {
+ if (strcmp(s, ":") == 0)
+ ++i;
+ else {
+ ts = strdup(s);
+ ATF_REQUIRE(ts != NULL);
+ sl_add(sl1, ts);
+ }
+ }
+ break;
+
+ case 4:
+ if (sl2 == NULL) {
+ if (strcmp(s, "(null)") == 0)
+ return (0);
+
+ sl2 = sl_init();
+ ATF_REQUIRE(sl2 != NULL);
+
+ if (strcmp(s, "noaddrs") != 0) {
+ ts = calloc(1, ht->h_length);
+ ATF_REQUIRE(ts != NULL);
+ rv = hostent_read_snapshot_addr(s,
+ (unsigned char *)ts,
+ ht->h_length);
+ sl_add(sl2, ts);
+ if (rv != 0)
+ goto fin;
+ }
+ } else {
+ ts = calloc(1, ht->h_length);
+ ATF_REQUIRE(ts != NULL);
+ rv = hostent_read_snapshot_addr(s,
+ (unsigned char *)ts, ht->h_length);
+ sl_add(sl2, ts);
+ if (rv != 0)
+ goto fin;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (i != 3 && i != 4)
+ ++i;
+ }
+
+fin:
+ if (sl1 != NULL) {
+ sl_add(sl1, NULL);
+ ht->h_aliases = sl1->sl_str;
+ }
+ if (sl2 != NULL) {
+ sl_add(sl2, NULL);
+ ht->h_addr_list = sl2->sl_str;
+ }
+
+ if ((i != 4) || (rv != 0)) {
+ free_hostent(ht);
+ memset(ht, 0, sizeof(struct hostent));
+ return (-1);
+ }
+
+ /* NOTE: is it a dirty hack or not? */
+ free(sl1);
+ free(sl2);
+ return (0);
+}
+
+static void
+dump_hostent(struct hostent *result)
+{
+ if (result != NULL) {
+ char buffer[1024];
+ sdump_hostent(result, buffer, sizeof(buffer));
+ printf("%s\n", buffer);
+ } else
+ printf("(null)\n");
+}
+
+static int
+hostent_test_correctness(struct hostent *ht, void *mdata __unused)
+{
+
+#ifdef DEBUG
+ printf("testing correctness with the following data:\n");
+ dump_hostent(ht);
+#endif
+
+ if (ht == NULL)
+ goto errfin;
+
+ if (ht->h_name == NULL)
+ goto errfin;
+
+ if (!((ht->h_addrtype >= 0) && (ht->h_addrtype < AF_MAX)))
+ goto errfin;
+
+ if ((ht->h_length != sizeof(struct in_addr)) &&
+ (ht->h_length != sizeof(struct in6_addr)))
+ goto errfin;
+
+ if (ht->h_aliases == NULL)
+ goto errfin;
+
+ if (ht->h_addr_list == NULL)
+ goto errfin;
+
+#ifdef DEBUG
+ printf("correct\n");
+#endif
+
+ return (0);
+errfin:
+ printf("incorrect\n");
+
+ return (-1);
+}
+
+static int
+hostent_test_gethostbyaddr(struct hostent *he, void *mdata)
+{
+ struct hostent *result;
+ struct hostent_test_data *addr_test_data;
+ int rv;
+
+ addr_test_data = (struct hostent_test_data *)mdata;
+
+ /* We should omit unresolved hostents */
+ if (he->h_addr_list != NULL) {
+ char **cp;
+ for (cp = he->h_addr_list; *cp; ++cp) {
+#ifdef DEBUG
+ printf("doing reverse lookup for %s\n", he->h_name);
+#endif
+
+ result = __gethostbyaddr(*cp, he->h_length,
+ he->h_addrtype);
+ if (result == NULL) {
+#ifdef DEBUG
+ printf("%s: warning: reverse lookup failed "
+ "for %s: %s\n", __func__, he->h_name,
+ strerror(errno));
+#endif
+ continue;
+ }
+ rv = hostent_test_correctness(result, NULL);
+ if (rv != 0) {
+ __freehostent(result);
+ return (rv);
+ }
+
+ if (addr_test_data != NULL)
+ TEST_DATA_APPEND(hostent, addr_test_data,
+ result);
+
+ __freehostent(result);
+ }
+ }
+
+ return (0);
+}
+
+static int
+hostent_test_getaddrinfo_eq(struct hostent *he, void *mdata __unused)
+{
+ struct addrinfo *ai, hints;
+ int rv;
+
+ ai = NULL;
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = af_type;
+ hints.ai_flags = AI_CANONNAME;
+
+ printf("using getaddrinfo() to resolve %s\n", he->h_name);
+
+ /* struct hostent *he was not resolved */
+ if (he->h_addr_list == NULL) {
+ /* We can be sure that he->h_name is not NULL */
+ rv = getaddrinfo(he->h_name, NULL, &hints, &ai);
+ if (rv == 0) {
+ printf("not ok - shouldn't have been resolved\n");
+ rv = -1;
+ } else
+ rv = 0;
+ } else {
+ rv = getaddrinfo(he->h_name, NULL, &hints, &ai);
+ if (rv != 0) {
+ printf("not ok - should have been resolved\n");
+ rv = -1;
+ goto done;
+ }
+ rv = is_hostent_equal(he, ai);
+ if (rv != 0) {
+ printf("not ok - addrinfo and hostent are not equal\n");
+ rv = -1;
+ }
+ }
+done:
+ if (ai != NULL)
+ freeaddrinfo(ai);
+ return (rv);
+}
+
+static int
+hostent_test_getnameinfo_eq(struct hostent *he, void *mdata __unused)
+{
+ char **cp;
+ char buffer[NI_MAXHOST];
+ struct sockaddr_in sin;
+ struct sockaddr_in6 sin6;
+ struct sockaddr *saddr;
+ struct hostent *result;
+ int i, rv;
+
+ if (he->h_addr_list == NULL)
+ return (0);
+
+ for (cp = he->h_addr_list; *cp; ++cp) {
+#ifdef DEBUG
+ printf("doing reverse lookup for %s\n", he->h_name);
+#endif
+ result = __gethostbyaddr(*cp, he->h_length,
+ he->h_addrtype);
+ if (result != NULL) {
+ rv = hostent_test_correctness(result, NULL);
+ if (rv != 0) {
+ __freehostent(result);
+ return (rv);
+ }
+ } else
+ printf("%s: warning: reverse lookup failed "
+ "for %s: %s\n", __func__, he->h_name,
+ strerror(errno));
+
+ switch (he->h_addrtype) {
+ case AF_INET:
+ memset(&sin, 0, sizeof(struct sockaddr_in));
+ sin.sin_len = sizeof(struct sockaddr_in);
+ sin.sin_family = AF_INET;
+ memcpy(&sin.sin_addr, *cp, he->h_length);
+
+ saddr = (struct sockaddr *)&sin;
+ break;
+ case AF_INET6:
+ memset(&sin6, 0, sizeof(struct sockaddr_in6));
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sin6.sin6_family = AF_INET6;
+ memcpy(&sin6.sin6_addr, *cp, he->h_length);
+
+ saddr = (struct sockaddr *)&sin6;
+ break;
+ default:
+ printf("warning: %d family is unsupported\n",
+ he->h_addrtype);
+ continue;
+ }
+
+ ATF_REQUIRE(saddr != NULL);
+ rv = getnameinfo(saddr, saddr->sa_len, buffer,
+ sizeof(buffer), NULL, 0, NI_NAMEREQD);
+
+ if (rv != 0 && result != NULL) {
+ printf("getnameinfo() didn't make the reverse "
+ "lookup, when it should have (%s)\n",
+ gai_strerror(rv));
+ return (rv);
+ }
+
+ if (rv == 0 && result == NULL) {
+ printf("getnameinfo() made the "
+ "reverse lookup, when it shouldn't have\n");
+ return (rv);
+ }
+
+ if (rv != 0 && result == NULL) {
+#ifdef DEBUG
+ printf("both getnameinfo() and ***byaddr() failed as "
+ "expected\n");
+#endif
+ continue;
+ }
+
+#ifdef DEBUG
+ printf("comparing %s with %s\n", result->h_name,
+ buffer);
+#endif
+
+ /*
+ * An address might reverse resolve to hostname alias or the
+ * official hostname, e.g. moon.vub.ac.be.
+ */
+ bool found_a_match = false;
+
+ if (strcmp(result->h_name, buffer) == 0) {
+ found_a_match = true;
+#ifdef DEBUG
+ printf("matched official hostname\n");
+#endif
+ } else {
+ for (i = 0; result->h_aliases[i] != NULL; i++) {
+ printf("[%d] resolved: %s\n", i,
+ result->h_aliases[i]);
+ if (strcmp(result->h_aliases[i],
+ buffer) == 0) {
+ printf("matched hostname alias\n");
+ found_a_match = true;
+ break;
+ }
+ }
+ }
+ __freehostent(result);
+
+ if (found_a_match) {
+#ifdef DEBUG
+ printf("getnameinfo() and ***byaddr() results are "
+ "equal\n");
+#endif
+ } else {
+ printf("getnameinfo() and ***byaddr() results are not "
+ "equal for %s\n", he->h_name);
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+run_tests(const char *hostlist_file, const char *snapshot_file, int _af_type,
+ enum test_methods method, bool use_ipv6_mapping)
+{
+ char *snapshot_file_copy;
+ struct hostent_test_data td, td_addr, td_snap;
+ res_state statp;
+ int rv = -2;
+
+ if (snapshot_file == NULL)
+ snapshot_file_copy = NULL;
+ else {
+ snapshot_file_copy = strdup(snapshot_file);
+ ATF_REQUIRE(snapshot_file_copy != NULL);
+ }
+ snapshot_file = snapshot_file_copy;
+
+ switch (_af_type) {
+ case AF_INET:
+ ATF_REQUIRE_FEATURE("inet");
+ ATF_REQUIRE(!use_ipv6_mapping);
+ break;
+ case AF_INET6:
+ ATF_REQUIRE_FEATURE("inet6");
+ break;
+ default:
+ atf_tc_fail("unhandled address family: %d", _af_type);
+ break;
+ }
+
+ if (!use_ipnode_functions) {
+ statp = __res_state();
+ if (statp == NULL || ((statp->options & RES_INIT) == 0 &&
+ res_ninit(statp) == -1)) {
+ printf("error: can't init res_state\n");
+ rv = -1;
+ goto fin2;
+ }
+
+ if (use_ipv6_mapping)
+ statp->options |= RES_USE_INET6;
+ else
+ statp->options &= ~RES_USE_INET6;
+ }
+
+ TEST_DATA_INIT(hostent, &td, clone_hostent, free_hostent);
+ TEST_DATA_INIT(hostent, &td_addr, clone_hostent, free_hostent);
+ TEST_DATA_INIT(hostent, &td_snap, clone_hostent, free_hostent);
+
+ if (access(hostlist_file, R_OK) != 0) {
+ printf("can't access the hostlist file %s\n", hostlist_file);
+ rv = -1;
+ goto fin;
+ }
+
+#ifdef DEBUG
+ printf("building host lists from %s\n", hostlist_file);
+#endif
+
+ rv = TEST_SNAPSHOT_FILE_READ(hostent, hostlist_file, &td,
+ hostent_read_hostlist_func);
+ if (rv != 0) {
+ printf("failed to read the host list file: %s\n",
+ hostlist_file);
+ goto fin;
+ }
+
+ if (snapshot_file != NULL) {
+ if (access(snapshot_file, W_OK | R_OK) != 0) {
+ if (errno == ENOENT) {
+ if (method != TEST_GETHOSTBYADDR)
+ method = TEST_BUILD_SNAPSHOT;
+ else
+ method = TEST_BUILD_ADDR_SNAPSHOT;
+ } else {
+ printf("can't access the snapshot file %s\n",
+ snapshot_file);
+ rv = -1;
+ goto fin;
+ }
+ } else {
+ rv = TEST_SNAPSHOT_FILE_READ(hostent, snapshot_file,
+ &td_snap, hostent_read_snapshot_func);
+ if (rv != 0) {
+ printf("error reading snapshot file\n");
+ goto fin;
+ }
+ }
+ }
+
+ switch (method) {
+ case TEST_GETHOSTBYNAME2:
+ if (snapshot_file != NULL)
+ rv = DO_2PASS_TEST(hostent, &td, &td_snap,
+ compare_hostent, NULL);
+ break;
+ case TEST_GETHOSTBYADDR:
+ rv = DO_1PASS_TEST(hostent, &td,
+ hostent_test_gethostbyaddr, (void *)&td_addr);
+ if (rv != 0)
+ goto fin;
+
+ if (snapshot_file != NULL)
+ rv = DO_2PASS_TEST(hostent, &td_addr, &td_snap,
+ compare_hostent, NULL);
+ break;
+ case TEST_GETHOSTBYNAME2_GETADDRINFO:
+ rv = DO_1PASS_TEST(hostent, &td,
+ hostent_test_getaddrinfo_eq, NULL);
+ break;
+ case TEST_GETHOSTBYADDR_GETNAMEINFO:
+ rv = DO_1PASS_TEST(hostent, &td,
+ hostent_test_getnameinfo_eq, NULL);
+ break;
+ case TEST_BUILD_SNAPSHOT:
+ if (snapshot_file != NULL) {
+ rv = TEST_SNAPSHOT_FILE_WRITE(hostent, snapshot_file,
+ &td, sdump_hostent);
+ }
+ break;
+ case TEST_BUILD_ADDR_SNAPSHOT:
+ if (snapshot_file != NULL) {
+ rv = DO_1PASS_TEST(hostent, &td,
+ hostent_test_gethostbyaddr, (void *)&td_addr);
+ if (rv != 0)
+ goto fin;
+ rv = TEST_SNAPSHOT_FILE_WRITE(hostent, snapshot_file,
+ &td_addr, sdump_hostent);
+ }
+ break;
+ default:
+ rv = 0;
+ break;
+ }
+
+fin:
+ TEST_DATA_DESTROY(hostent, &td_snap);
+ TEST_DATA_DESTROY(hostent, &td_addr);
+ TEST_DATA_DESTROY(hostent, &td);
+
+fin2:
+ free(snapshot_file_copy);
+
+ return (rv);
+}
+
+#define HOSTLIST_FILE "mach"
+
+#define _RUN_TESTS(tc, snapshot_file, af_type, method, use_ipv6_mapping) \
+do { \
+ char *_hostlist_file; \
+ ATF_REQUIRE(0 < asprintf(&_hostlist_file, "%s/%s", \
+ atf_tc_get_config_var(tc, "srcdir"), HOSTLIST_FILE)); \
+ ATF_REQUIRE(run_tests(_hostlist_file, snapshot_file, af_type, \
+ method, use_ipv6_mapping) == 0); \
+ free(_hostlist_file); \
+} while (0)
+
+#define RUN_HOST_TESTS(tc, snapshot_file, af_type, method, use_ipv6_mapping) \
+do { \
+ use_ipnode_functions = false; \
+ _RUN_TESTS(tc, snapshot_file, af_type, method, use_ipv6_mapping); \
+} while (0)
+
+#define RUN_IPNODE_TESTS(tc, snapshot_file, af_type, method, use_ipv6_mapping) \
+do { \
+ use_ipnode_functions = true; \
+ _RUN_TESTS(tc, snapshot_file, af_type, method, use_ipv6_mapping); \
+} while (0)
+
+ATF_TC_WITHOUT_HEAD(gethostbyaddr_ipv4);
+ATF_TC_BODY(gethostbyaddr_ipv4, tc)
+{
+
+ RUN_HOST_TESTS(tc, NULL, AF_INET, TEST_GETHOSTBYADDR, false);
+}
+
+ATF_TC_WITHOUT_HEAD(gethostbyaddr_ipv4_with_snapshot);
+ATF_TC_BODY(gethostbyaddr_ipv4_with_snapshot, tc)
+{
+
+ RUN_HOST_TESTS(tc, "snapshot_htaddr4", AF_INET, TEST_GETHOSTBYADDR, false);
+}
+
+ATF_TC_WITHOUT_HEAD(gethostbyaddr_ipv6);
+ATF_TC_BODY(gethostbyaddr_ipv6, tc)
+{
+
+ RUN_HOST_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYADDR, false);
+}
+
+ATF_TC_WITHOUT_HEAD(gethostbyaddr_ipv6_AI_V4MAPPED);
+ATF_TC_BODY(gethostbyaddr_ipv6_AI_V4MAPPED, tc)
+{
+
+ ipnode_flags = AI_V4MAPPED;
+ RUN_HOST_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYADDR, true);
+}
+
+ATF_TC_WITHOUT_HEAD(gethostbyaddr_ipv6_with_snapshot);
+ATF_TC_BODY(gethostbyaddr_ipv6_with_snapshot, tc)
+{
+
+ RUN_HOST_TESTS(tc, "snapshot_htaddr6", AF_INET6, TEST_GETHOSTBYADDR, false);
+}
+
+ATF_TC_WITHOUT_HEAD(gethostbyaddr_ipv6_with_snapshot_AI_V4MAPPED);
+ATF_TC_BODY(gethostbyaddr_ipv6_with_snapshot_AI_V4MAPPED, tc)
+{
+
+ ipnode_flags = AI_V4MAPPED;
+ RUN_HOST_TESTS(tc, "snapshot_htaddr6map", AF_INET6, TEST_GETHOSTBYADDR, true);
+}
+
+ATF_TC_WITHOUT_HEAD(gethostbyname2_getaddrinfo_ipv4);
+ATF_TC_BODY(gethostbyname2_getaddrinfo_ipv4, tc)
+{
+
+ RUN_HOST_TESTS(tc, NULL, AF_INET, TEST_GETHOSTBYNAME2_GETADDRINFO, false);
+}
+
+ATF_TC_WITHOUT_HEAD(gethostbyname2_getaddrinfo_ipv6);
+ATF_TC_BODY(gethostbyname2_getaddrinfo_ipv6, tc)
+{
+
+ RUN_HOST_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2_GETADDRINFO, false);
+}
+
+ATF_TC_WITHOUT_HEAD(gethostbyaddr_getnameinfo_ipv4);
+ATF_TC_BODY(gethostbyaddr_getnameinfo_ipv4, tc)
+{
+
+ RUN_HOST_TESTS(tc, NULL, AF_INET, TEST_GETHOSTBYADDR_GETNAMEINFO, false);
+}
+
+ATF_TC_WITHOUT_HEAD(gethostbyaddr_getnameinfo_ipv6);
+ATF_TC_BODY(gethostbyaddr_getnameinfo_ipv6, tc)
+{
+
+ RUN_HOST_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYADDR_GETNAMEINFO, false);
+}
+
+ATF_TC_WITHOUT_HEAD(gethostbyname2_ipv4);
+ATF_TC_BODY(gethostbyname2_ipv4, tc)
+{
+
+ RUN_HOST_TESTS(tc, NULL, AF_INET, TEST_GETHOSTBYNAME2, false);
+}
+
+ATF_TC_WITHOUT_HEAD(gethostbyname2_ipv4_with_snapshot);
+ATF_TC_BODY(gethostbyname2_ipv4_with_snapshot, tc)
+{
+
+ RUN_HOST_TESTS(tc, "snapshot_htname4", AF_INET, TEST_GETHOSTBYNAME2, false);
+}
+
+ATF_TC_WITHOUT_HEAD(gethostbyname2_ipv6);
+ATF_TC_BODY(gethostbyname2_ipv6, tc)
+{
+
+ RUN_HOST_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2, false);
+}
+
+ATF_TC_WITHOUT_HEAD(gethostbyname2_ipv6_AI_V4MAPPED);
+ATF_TC_BODY(gethostbyname2_ipv6_AI_V4MAPPED, tc)
+{
+
+ ipnode_flags = AI_V4MAPPED;
+ RUN_HOST_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2, true);
+}
+
+ATF_TC_WITHOUT_HEAD(gethostbyname2_ipv6_with_snapshot);
+ATF_TC_BODY(gethostbyname2_ipv6_with_snapshot, tc)
+{
+
+ RUN_HOST_TESTS(tc, "snapshot_htname6", AF_INET6, TEST_GETHOSTBYNAME2, false);
+}
+
+ATF_TC_WITHOUT_HEAD(gethostbyname2_ipv6_with_snapshot_AI_V4MAPPED);
+ATF_TC_BODY(gethostbyname2_ipv6_with_snapshot_AI_V4MAPPED, tc)
+{
+
+ ipnode_flags = AI_V4MAPPED;
+ RUN_HOST_TESTS(tc, "snapshot_htname6map", AF_INET6, TEST_GETHOSTBYNAME2, true);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv4);
+ATF_TC_BODY(getipnodebyaddr_ipv4, tc)
+{
+
+ RUN_IPNODE_TESTS(tc, NULL, AF_INET, TEST_GETHOSTBYADDR, false);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv4_with_snapshot);
+ATF_TC_BODY(getipnodebyaddr_ipv4_with_snapshot, tc)
+{
+
+ RUN_IPNODE_TESTS(tc, "snapshot_ipnodeaddr4", AF_INET, TEST_GETHOSTBYADDR, false);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyaddr_getnameinfo_ipv4);
+ATF_TC_BODY(getipnodebyaddr_getnameinfo_ipv4, tc)
+{
+
+ RUN_IPNODE_TESTS(tc, NULL, AF_INET, TEST_GETHOSTBYADDR_GETNAMEINFO, false);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv6);
+ATF_TC_BODY(getipnodebyaddr_ipv6, tc)
+{
+
+ RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYADDR, false);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv6_AI_V4MAPPED);
+ATF_TC_BODY(getipnodebyaddr_ipv6_AI_V4MAPPED, tc)
+{
+
+ ipnode_flags = AI_V4MAPPED;
+ RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYADDR, true);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv6_AI_V4MAPPED_CFG);
+ATF_TC_BODY(getipnodebyaddr_ipv6_AI_V4MAPPED_CFG, tc)
+{
+
+ ipnode_flags = AI_V4MAPPED_CFG;
+ RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYADDR, true);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv6_AI_V4MAPPED_CFG_AI_ALL);
+ATF_TC_BODY(getipnodebyaddr_ipv6_AI_V4MAPPED_CFG_AI_ALL, tc)
+{
+
+ ipnode_flags = AI_V4MAPPED_CFG | AI_ALL;
+ RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYADDR, true);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv6_with_snapshot);
+ATF_TC_BODY(getipnodebyaddr_ipv6_with_snapshot, tc)
+{
+
+ RUN_IPNODE_TESTS(tc, "snapshot_ipnodeaddr6", AF_INET6, TEST_GETHOSTBYADDR, false);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv6_with_snapshot_AI_V4MAPPED);
+ATF_TC_BODY(getipnodebyaddr_ipv6_with_snapshot_AI_V4MAPPED, tc)
+{
+
+ ipnode_flags = AI_V4MAPPED;
+ RUN_IPNODE_TESTS(tc,
+ "snapshot_ipnodeaddr6_AI_V4MAPPED", AF_INET6,
+ TEST_GETHOSTBYADDR, true);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv6_with_snapshot_AI_V4MAPPED_CFG);
+ATF_TC_BODY(getipnodebyaddr_ipv6_with_snapshot_AI_V4MAPPED_CFG, tc)
+{
+
+ ipnode_flags = AI_V4MAPPED_CFG;
+ RUN_IPNODE_TESTS(tc,
+ "snapshot_ipnodeaddr6_AI_V4MAPPED_CFG", AF_INET6,
+ TEST_GETHOSTBYADDR, true);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyaddr_ipv6_with_snapshot_AI_V4MAPPED_CFG_AI_ALL);
+ATF_TC_BODY(getipnodebyaddr_ipv6_with_snapshot_AI_V4MAPPED_CFG_AI_ALL, tc)
+{
+
+ ipnode_flags = AI_V4MAPPED_CFG | AI_ALL;
+ RUN_IPNODE_TESTS(tc,
+ "snapshot_ipnodeaddr6_AI_V4MAPPED_CFG_AI_ALL", AF_INET6,
+ TEST_GETHOSTBYADDR, true);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyaddr_getnameinfo_ipv6);
+ATF_TC_BODY(getipnodebyaddr_getnameinfo_ipv6, tc)
+{
+
+ RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYADDR_GETNAMEINFO, false);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv4);
+ATF_TC_BODY(getipnodebyname_ipv4, tc)
+{
+
+ RUN_IPNODE_TESTS(tc, NULL, AF_INET, TEST_GETHOSTBYNAME2, false);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv4_with_snapshot);
+ATF_TC_BODY(getipnodebyname_ipv4_with_snapshot, tc)
+{
+
+ RUN_IPNODE_TESTS(tc, "snapshot_ipnodename4", AF_INET, TEST_GETHOSTBYNAME2, false);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv4_AI_ADDRCONFIG);
+ATF_TC_BODY(getipnodebyname_ipv4_AI_ADDRCONFIG, tc)
+{
+
+ ipnode_flags = AI_ADDRCONFIG;
+ RUN_IPNODE_TESTS(tc, NULL, AF_INET, TEST_GETHOSTBYNAME2, false);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv4_with_snapshot_AI_ADDRCONFIG);
+ATF_TC_BODY(getipnodebyname_ipv4_with_snapshot_AI_ADDRCONFIG, tc)
+{
+
+ ipnode_flags = AI_ADDRCONFIG;
+ RUN_IPNODE_TESTS(tc, "snapshot_ipnodename4_AI_ADDRCONFIG", AF_INET,
+ TEST_GETHOSTBYNAME2, false);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyname_getaddrinfo_ipv4);
+ATF_TC_BODY(getipnodebyname_getaddrinfo_ipv4, tc)
+{
+
+ RUN_IPNODE_TESTS(tc, NULL, AF_INET, TEST_GETHOSTBYNAME2_GETADDRINFO, false);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6);
+ATF_TC_BODY(getipnodebyname_ipv6, tc)
+{
+
+ RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2, false);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_with_snapshot);
+ATF_TC_BODY(getipnodebyname_ipv6_with_snapshot, tc)
+{
+
+ RUN_IPNODE_TESTS(tc, "snapshot_ipnodename6", AF_INET6, TEST_GETHOSTBYNAME2, false);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_AI_ADDRCONFIG);
+ATF_TC_BODY(getipnodebyname_ipv6_AI_ADDRCONFIG, tc)
+{
+
+ ipnode_flags = AI_ADDRCONFIG;
+ RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2, false);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_AI_V4MAPPED);
+ATF_TC_BODY(getipnodebyname_ipv6_AI_V4MAPPED, tc)
+{
+
+ ipnode_flags = AI_V4MAPPED;
+ RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2, true);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_AI_V4MAPPED_CFG);
+ATF_TC_BODY(getipnodebyname_ipv6_AI_V4MAPPED_CFG, tc)
+{
+
+ ipnode_flags = AI_V4MAPPED_CFG;
+ RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2, true);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_AI_V4MAPPED_CFG_AI_ADDRCONFIG);
+ATF_TC_BODY(getipnodebyname_ipv6_AI_V4MAPPED_CFG_AI_ADDRCONFIG, tc)
+{
+
+ ipnode_flags = AI_V4MAPPED_CFG | AI_ADDRCONFIG;
+ RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2, false);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_AI_V4MAPPED_CFG_AI_ALL);
+ATF_TC_BODY(getipnodebyname_ipv6_AI_V4MAPPED_CFG_AI_ALL, tc)
+{
+
+ ipnode_flags = AI_V4MAPPED_CFG | AI_ALL;
+ RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2, true);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED);
+ATF_TC_BODY(getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED, tc)
+{
+
+ ipnode_flags = AI_V4MAPPED;
+ RUN_IPNODE_TESTS(tc,
+ "snapshot_ipnodename6_AI_V4MAPPED", AF_INET6,
+ TEST_GETHOSTBYNAME2, true);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED_CFG);
+ATF_TC_BODY(getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED_CFG, tc)
+{
+
+ ipnode_flags = AI_V4MAPPED_CFG;
+ RUN_IPNODE_TESTS(tc,
+ "snapshot_ipnodename6_AI_V4MAPPED_CFG", AF_INET6,
+ TEST_GETHOSTBYNAME2, true);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED_CFG_AI_ADDRCONFIG);
+ATF_TC_BODY(getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED_CFG_AI_ADDRCONFIG, tc)
+{
+
+ ipnode_flags = AI_V4MAPPED_CFG | AI_ADDRCONFIG;
+ RUN_IPNODE_TESTS(tc,
+ "snapshot_ipnodename6_AI_V4MAPPED_CFG_AI_ADDRCONFIG", AF_INET6,
+ TEST_GETHOSTBYNAME2, false);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED_CFG_AI_ALL);
+ATF_TC_BODY(getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED_CFG_AI_ALL, tc)
+{
+
+ ipnode_flags = AI_V4MAPPED_CFG | AI_ALL;
+ RUN_IPNODE_TESTS(tc,
+ "snapshot_ipnodename6_AI_V4MAPPED_CFG_AI_ALL", AF_INET6,
+ TEST_GETHOSTBYNAME2, true);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyname_ipv6_with_snapshot_AI_ADDRCONFIG);
+ATF_TC_BODY(getipnodebyname_ipv6_with_snapshot_AI_ADDRCONFIG, tc)
+{
+
+ ipnode_flags = AI_ADDRCONFIG;
+ RUN_IPNODE_TESTS(tc, "snapshot_ipnodename6_AI_ADDRCONFIG", AF_INET6,
+ TEST_GETHOSTBYNAME2, false);
+}
+
+ATF_TC_WITHOUT_HEAD(getipnodebyname_getaddrinfo_ipv6);
+ATF_TC_BODY(getipnodebyname_getaddrinfo_ipv6, tc)
+{
+
+ RUN_IPNODE_TESTS(tc, NULL, AF_INET6, TEST_GETHOSTBYNAME2_GETADDRINFO, false);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ /* gethostbyaddr */
+ ATF_TP_ADD_TC(tp, gethostbyaddr_ipv4);
+ ATF_TP_ADD_TC(tp, gethostbyaddr_ipv4_with_snapshot);
+ ATF_TP_ADD_TC(tp, gethostbyaddr_ipv6);
+ ATF_TP_ADD_TC(tp, gethostbyaddr_ipv6_AI_V4MAPPED); /* XXX */
+ ATF_TP_ADD_TC(tp, gethostbyaddr_ipv6_with_snapshot);
+ ATF_TP_ADD_TC(tp, gethostbyaddr_ipv6_with_snapshot_AI_V4MAPPED);
+ ATF_TP_ADD_TC(tp, gethostbyaddr_getnameinfo_ipv4);
+ ATF_TP_ADD_TC(tp, gethostbyaddr_getnameinfo_ipv6);
+
+ /* gethostbyname2 */
+ ATF_TP_ADD_TC(tp, gethostbyname2_getaddrinfo_ipv4);
+ ATF_TP_ADD_TC(tp, gethostbyname2_getaddrinfo_ipv6);
+ ATF_TP_ADD_TC(tp, gethostbyname2_ipv4);
+ ATF_TP_ADD_TC(tp, gethostbyname2_ipv4_with_snapshot);
+ ATF_TP_ADD_TC(tp, gethostbyname2_ipv6);
+ ATF_TP_ADD_TC(tp, gethostbyname2_ipv6_AI_V4MAPPED);
+ ATF_TP_ADD_TC(tp, gethostbyname2_ipv6_with_snapshot);
+ ATF_TP_ADD_TC(tp, gethostbyname2_ipv6_with_snapshot_AI_V4MAPPED);
+
+ /* getipnodebyaddr */
+ ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv4);
+ ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv4_with_snapshot);
+ ATF_TP_ADD_TC(tp, getipnodebyaddr_getnameinfo_ipv4);
+ ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv6);
+ ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv6_AI_V4MAPPED);
+ ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv6_AI_V4MAPPED_CFG);
+ ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv6_AI_V4MAPPED_CFG_AI_ALL);
+ ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv6_with_snapshot);
+ ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv6_with_snapshot_AI_V4MAPPED);
+ ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv6_with_snapshot_AI_V4MAPPED_CFG);
+ ATF_TP_ADD_TC(tp, getipnodebyaddr_ipv6_with_snapshot_AI_V4MAPPED_CFG_AI_ALL);
+ ATF_TP_ADD_TC(tp, getipnodebyaddr_getnameinfo_ipv6);
+
+ /* getipnodebyname */
+ ATF_TP_ADD_TC(tp, getipnodebyname_ipv4);
+ ATF_TP_ADD_TC(tp, getipnodebyname_ipv4_with_snapshot);
+ ATF_TP_ADD_TC(tp, getipnodebyname_ipv4_AI_ADDRCONFIG);
+ ATF_TP_ADD_TC(tp, getipnodebyname_ipv4_with_snapshot_AI_ADDRCONFIG);
+ ATF_TP_ADD_TC(tp, getipnodebyname_getaddrinfo_ipv4);
+ ATF_TP_ADD_TC(tp, getipnodebyname_ipv6);
+ ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_with_snapshot);
+ ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_AI_ADDRCONFIG);
+ ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_AI_V4MAPPED);
+ ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_AI_V4MAPPED_CFG);
+ ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_AI_V4MAPPED_CFG_AI_ADDRCONFIG);
+ ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_AI_V4MAPPED_CFG_AI_ALL);
+ ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED);
+ ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED_CFG);
+ ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED_CFG_AI_ADDRCONFIG);
+ ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_with_snapshot_AI_V4MAPPED_CFG_AI_ALL);
+ ATF_TP_ADD_TC(tp, getipnodebyname_ipv6_with_snapshot_AI_ADDRCONFIG);
+ ATF_TP_ADD_TC(tp, getipnodebyname_getaddrinfo_ipv6);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/nss/getproto_test.c b/lib/libc/tests/nss/getproto_test.c
new file mode 100644
index 000000000000..9b8250e47032
--- /dev/null
+++ b/lib/libc/tests/nss/getproto_test.c
@@ -0,0 +1,553 @@
+/*-
+ * Copyright (c) 2006 Michael Bushkov <bushman@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stringlist.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "testutil.h"
+
+enum test_methods {
+ TEST_GETPROTOENT,
+ TEST_GETPROTOBYNAME,
+ TEST_GETPROTOBYNUMBER,
+ TEST_GETPROTOENT_2PASS,
+ TEST_BUILD_SNAPSHOT
+};
+
+DECLARE_TEST_DATA(protoent)
+DECLARE_TEST_FILE_SNAPSHOT(protoent)
+DECLARE_1PASS_TEST(protoent)
+DECLARE_2PASS_TEST(protoent)
+
+static void clone_protoent(struct protoent *, struct protoent const *);
+static int compare_protoent(struct protoent *, struct protoent *, void *);
+static void dump_protoent(struct protoent *);
+static void free_protoent(struct protoent *);
+
+static void sdump_protoent(struct protoent *, char *, size_t);
+static int protoent_read_snapshot_func(struct protoent *, char *);
+
+static int protoent_check_ambiguity(struct protoent_test_data *,
+ struct protoent *);
+static int protoent_fill_test_data(struct protoent_test_data *);
+static int protoent_test_correctness(struct protoent *, void *);
+static int protoent_test_getprotobyname(struct protoent *, void *);
+static int protoent_test_getprotobynumber(struct protoent *, void *);
+static int protoent_test_getprotoent(struct protoent *, void *);
+
+IMPLEMENT_TEST_DATA(protoent)
+IMPLEMENT_TEST_FILE_SNAPSHOT(protoent)
+IMPLEMENT_1PASS_TEST(protoent)
+IMPLEMENT_2PASS_TEST(protoent)
+
+static void
+clone_protoent(struct protoent *dest, struct protoent const *src)
+{
+ assert(dest != NULL);
+ assert(src != NULL);
+
+ char **cp;
+ int aliases_num;
+
+ memset(dest, 0, sizeof(struct protoent));
+
+ if (src->p_name != NULL) {
+ dest->p_name = strdup(src->p_name);
+ assert(dest->p_name != NULL);
+ }
+
+ dest->p_proto = src->p_proto;
+
+ if (src->p_aliases != NULL) {
+ aliases_num = 0;
+ for (cp = src->p_aliases; *cp; ++cp)
+ ++aliases_num;
+
+ dest->p_aliases = calloc(aliases_num + 1, sizeof(char *));
+ assert(dest->p_aliases != NULL);
+
+ for (cp = src->p_aliases; *cp; ++cp) {
+ dest->p_aliases[cp - src->p_aliases] = strdup(*cp);
+ assert(dest->p_aliases[cp - src->p_aliases] != NULL);
+ }
+ }
+}
+
+static void
+free_protoent(struct protoent *pe)
+{
+ char **cp;
+
+ assert(pe != NULL);
+
+ free(pe->p_name);
+
+ for (cp = pe->p_aliases; *cp; ++cp)
+ free(*cp);
+ free(pe->p_aliases);
+}
+
+static int
+compare_protoent(struct protoent *pe1, struct protoent *pe2, void *mdata)
+{
+ char **c1, **c2;
+
+ if (pe1 == pe2)
+ return 0;
+
+ if ((pe1 == NULL) || (pe2 == NULL))
+ goto errfin;
+
+ if ((strcmp(pe1->p_name, pe2->p_name) != 0) ||
+ (pe1->p_proto != pe2->p_proto))
+ goto errfin;
+
+ c1 = pe1->p_aliases;
+ c2 = pe2->p_aliases;
+
+ if ((pe1->p_aliases == NULL) || (pe2->p_aliases == NULL))
+ goto errfin;
+
+ for (;*c1 && *c2; ++c1, ++c2)
+ if (strcmp(*c1, *c2) != 0)
+ goto errfin;
+
+ if ((*c1 != NULL) || (*c2 != NULL))
+ goto errfin;
+
+ return 0;
+
+errfin:
+ if (mdata == NULL) {
+ printf("following structures are not equal:\n");
+ dump_protoent(pe1);
+ dump_protoent(pe2);
+ }
+
+ return (-1);
+}
+
+static void
+sdump_protoent(struct protoent *pe, char *buffer, size_t buflen)
+{
+ char **cp;
+ int written;
+
+ written = snprintf(buffer, buflen, "%s %d",
+ pe->p_name, pe->p_proto);
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+
+ if (pe->p_aliases != NULL) {
+ if (*(pe->p_aliases) != NULL) {
+ for (cp = pe->p_aliases; *cp; ++cp) {
+ written = snprintf(buffer, buflen, " %s", *cp);
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+
+ if (buflen == 0)
+ return;
+ }
+ } else
+ snprintf(buffer, buflen, " noaliases");
+ } else
+ snprintf(buffer, buflen, " (null)");
+}
+
+static int
+protoent_read_snapshot_func(struct protoent *pe, char *line)
+{
+ StringList *sl;
+ char *s, *ps, *ts;
+ int i;
+
+ printf("1 line read from snapshot:\n%s\n", line);
+
+ i = 0;
+ sl = NULL;
+ ps = line;
+ memset(pe, 0, sizeof(struct protoent));
+ while ( (s = strsep(&ps, " ")) != NULL) {
+ switch (i) {
+ case 0:
+ pe->p_name = strdup(s);
+ assert(pe->p_name != NULL);
+ break;
+
+ case 1:
+ pe->p_proto = (int)strtol(s, &ts, 10);
+ if (*ts != '\0') {
+ free(pe->p_name);
+ return (-1);
+ }
+ break;
+
+ default:
+ if (sl == NULL) {
+ if (strcmp(s, "(null)") == 0)
+ return (0);
+
+ sl = sl_init();
+ assert(sl != NULL);
+
+ if (strcmp(s, "noaliases") != 0) {
+ ts = strdup(s);
+ assert(ts != NULL);
+ sl_add(sl, ts);
+ }
+ } else {
+ ts = strdup(s);
+ assert(ts != NULL);
+ sl_add(sl, ts);
+ }
+ break;
+ }
+ ++i;
+ }
+
+ if (i < 3) {
+ free(pe->p_name);
+ memset(pe, 0, sizeof(struct protoent));
+ return (-1);
+ }
+
+ sl_add(sl, NULL);
+ pe->p_aliases = sl->sl_str;
+
+ /* NOTE: is it a dirty hack or not? */
+ free(sl);
+ return (0);
+}
+
+static void
+dump_protoent(struct protoent *result)
+{
+ if (result != NULL) {
+ char buffer[1024];
+ sdump_protoent(result, buffer, sizeof(buffer));
+ printf("%s\n", buffer);
+ } else
+ printf("(null)\n");
+}
+
+static int
+protoent_fill_test_data(struct protoent_test_data *td)
+{
+ struct protoent *pe;
+
+ setprotoent(1);
+ while ((pe = getprotoent()) != NULL) {
+ if (protoent_test_correctness(pe, NULL) == 0)
+ TEST_DATA_APPEND(protoent, td, pe);
+ else
+ return (-1);
+ }
+ endprotoent();
+
+ return (0);
+}
+
+static int
+protoent_test_correctness(struct protoent *pe, void *mdata __unused)
+{
+ printf("testing correctness with the following data:\n");
+ dump_protoent(pe);
+
+ if (pe == NULL)
+ goto errfin;
+
+ if (pe->p_name == NULL)
+ goto errfin;
+
+ if (pe->p_proto < 0)
+ goto errfin;
+
+ if (pe->p_aliases == NULL)
+ goto errfin;
+
+ printf("correct\n");
+
+ return (0);
+errfin:
+ printf("incorrect\n");
+
+ return (-1);
+}
+
+/* protoent_check_ambiguity() is needed when one port+proto is associated with
+ * more than one piece (these cases are usually marked as PROBLEM in
+ * /etc/peices. This functions is needed also when one piece+proto is
+ * associated with several ports. We have to check all the protoent structures
+ * to make sure that pe really exists and correct */
+static int
+protoent_check_ambiguity(struct protoent_test_data *td, struct protoent *pe)
+{
+
+ return (TEST_DATA_FIND(protoent, td, pe, compare_protoent,
+ NULL) != NULL ? 0 : -1);
+}
+
+static int
+protoent_test_getprotobyname(struct protoent *pe_model, void *mdata)
+{
+ char **alias;
+ struct protoent *pe;
+
+ printf("testing getprotobyname() with the following data:\n");
+ dump_protoent(pe_model);
+
+ pe = getprotobyname(pe_model->p_name);
+ if (protoent_test_correctness(pe, NULL) != 0)
+ goto errfin;
+
+ if ((compare_protoent(pe, pe_model, NULL) != 0) &&
+ (protoent_check_ambiguity((struct protoent_test_data *)mdata, pe)
+ !=0))
+ goto errfin;
+
+ for (alias = pe_model->p_aliases; *alias; ++alias) {
+ pe = getprotobyname(*alias);
+
+ if (protoent_test_correctness(pe, NULL) != 0)
+ goto errfin;
+
+ if ((compare_protoent(pe, pe_model, NULL) != 0) &&
+ (protoent_check_ambiguity(
+ (struct protoent_test_data *)mdata, pe) != 0))
+ goto errfin;
+ }
+
+ printf("ok\n");
+ return (0);
+
+errfin:
+ printf("not ok\n");
+
+ return (-1);
+}
+
+static int
+protoent_test_getprotobynumber(struct protoent *pe_model, void *mdata)
+{
+ struct protoent *pe;
+
+ printf("testing getprotobyport() with the following data...\n");
+ dump_protoent(pe_model);
+
+ pe = getprotobynumber(pe_model->p_proto);
+ if ((protoent_test_correctness(pe, NULL) != 0) ||
+ ((compare_protoent(pe, pe_model, NULL) != 0) &&
+ (protoent_check_ambiguity((struct protoent_test_data *)mdata, pe)
+ != 0))) {
+ printf("not ok\n");
+ return (-1);
+ } else {
+ printf("ok\n");
+ return (0);
+ }
+}
+
+static int
+protoent_test_getprotoent(struct protoent *pe, void *mdata __unused)
+{
+ /* Only correctness can be checked when doing 1-pass test for
+ * getprotoent(). */
+ return (protoent_test_correctness(pe, NULL));
+}
+
+static int
+run_tests(const char *snapshot_file, enum test_methods method)
+{
+ struct protoent_test_data td, td_snap, td_2pass;
+ int rv;
+
+ TEST_DATA_INIT(protoent, &td, clone_protoent, free_protoent);
+ TEST_DATA_INIT(protoent, &td_snap, clone_protoent, free_protoent);
+ if (snapshot_file != NULL) {
+ if (access(snapshot_file, W_OK | R_OK) != 0) {
+ if (errno == ENOENT)
+ method = TEST_BUILD_SNAPSHOT;
+ else {
+ printf("can't access the file %s\n",
+ snapshot_file);
+
+ rv = -1;
+ goto fin;
+ }
+ } else {
+ if (method == TEST_BUILD_SNAPSHOT) {
+ rv = 0;
+ goto fin;
+ }
+
+ TEST_SNAPSHOT_FILE_READ(protoent, snapshot_file,
+ &td_snap, protoent_read_snapshot_func);
+ }
+ }
+
+ rv = protoent_fill_test_data(&td);
+ if (rv == -1)
+ return (-1);
+ switch (method) {
+ case TEST_GETPROTOBYNAME:
+ if (snapshot_file == NULL)
+ rv = DO_1PASS_TEST(protoent, &td,
+ protoent_test_getprotobyname, (void *)&td);
+ else
+ rv = DO_1PASS_TEST(protoent, &td_snap,
+ protoent_test_getprotobyname, (void *)&td_snap);
+ break;
+ case TEST_GETPROTOBYNUMBER:
+ if (snapshot_file == NULL)
+ rv = DO_1PASS_TEST(protoent, &td,
+ protoent_test_getprotobynumber, (void *)&td);
+ else
+ rv = DO_1PASS_TEST(protoent, &td_snap,
+ protoent_test_getprotobynumber, (void *)&td_snap);
+ break;
+ case TEST_GETPROTOENT:
+ if (snapshot_file == NULL)
+ rv = DO_1PASS_TEST(protoent, &td,
+ protoent_test_getprotoent, (void *)&td);
+ else
+ rv = DO_2PASS_TEST(protoent, &td, &td_snap,
+ compare_protoent, NULL);
+ break;
+ case TEST_GETPROTOENT_2PASS:
+ TEST_DATA_INIT(protoent, &td_2pass, clone_protoent,
+ free_protoent);
+ rv = protoent_fill_test_data(&td_2pass);
+ if (rv != -1)
+ rv = DO_2PASS_TEST(protoent, &td, &td_2pass,
+ compare_protoent, NULL);
+ TEST_DATA_DESTROY(protoent, &td_2pass);
+ break;
+ case TEST_BUILD_SNAPSHOT:
+ if (snapshot_file != NULL)
+ rv = TEST_SNAPSHOT_FILE_WRITE(protoent, snapshot_file,
+ &td, sdump_protoent);
+ break;
+ default:
+ rv = 0;
+ break;
+ }
+
+fin:
+ TEST_DATA_DESTROY(protoent, &td_snap);
+ TEST_DATA_DESTROY(protoent, &td);
+
+ return (rv);
+}
+
+#define SNAPSHOT_FILE "snapshot_proto"
+
+ATF_TC_WITHOUT_HEAD(build_snapshot);
+ATF_TC_BODY(build_snapshot, tc)
+{
+
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getprotoent);
+ATF_TC_BODY(getprotoent, tc)
+{
+
+ ATF_REQUIRE(run_tests(NULL, TEST_GETPROTOENT) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getprotoent_with_snapshot);
+ATF_TC_BODY(getprotoent_with_snapshot, tc)
+{
+
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETPROTOENT) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getprotoent_with_two_pass);
+ATF_TC_BODY(getprotoent_with_two_pass, tc)
+{
+
+ ATF_REQUIRE(run_tests(NULL, TEST_GETPROTOENT_2PASS) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getprotobyname);
+ATF_TC_BODY(getprotobyname, tc)
+{
+
+ ATF_REQUIRE(run_tests(NULL, TEST_GETPROTOBYNAME) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getprotobyname_with_snapshot);
+ATF_TC_BODY(getprotobyname_with_snapshot, tc)
+{
+
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETPROTOBYNAME) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getprotobynumber);
+ATF_TC_BODY(getprotobynumber, tc)
+{
+
+ ATF_REQUIRE(run_tests(NULL, TEST_GETPROTOBYNUMBER) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getprotobynumber_with_snapshot);
+ATF_TC_BODY(getprotobynumber_with_snapshot, tc)
+{
+
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETPROTOBYNUMBER) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, build_snapshot);
+ ATF_TP_ADD_TC(tp, getprotoent);
+ ATF_TP_ADD_TC(tp, getprotoent_with_snapshot);
+ ATF_TP_ADD_TC(tp, getprotoent_with_two_pass);
+ ATF_TP_ADD_TC(tp, getprotobyname);
+ ATF_TP_ADD_TC(tp, getprotobyname_with_snapshot);
+ ATF_TP_ADD_TC(tp, getprotobynumber);
+ ATF_TP_ADD_TC(tp, getprotobynumber_with_snapshot);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/nss/getpw_test.c b/lib/libc/tests/nss/getpw_test.c
new file mode 100644
index 000000000000..434d86a31591
--- /dev/null
+++ b/lib/libc/tests/nss/getpw_test.c
@@ -0,0 +1,555 @@
+/*-
+ * Copyright (c) 2006 Michael Bushkov <bushman@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "testutil.h"
+
+enum test_methods {
+ TEST_GETPWENT,
+ TEST_GETPWENT_INTERLEAVED_GETPWNAM,
+ TEST_GETPWENT_INTERLEAVED_GETPWUID,
+ TEST_GETPWNAM,
+ TEST_GETPWUID,
+ TEST_GETPWENT_2PASS,
+ TEST_BUILD_SNAPSHOT
+};
+
+DECLARE_TEST_DATA(passwd)
+DECLARE_TEST_FILE_SNAPSHOT(passwd)
+DECLARE_1PASS_TEST(passwd)
+DECLARE_2PASS_TEST(passwd)
+
+static void clone_passwd(struct passwd *, struct passwd const *);
+static int compare_passwd(struct passwd *, struct passwd *, void *);
+static void free_passwd(struct passwd *);
+
+static void sdump_passwd(struct passwd *, char *, size_t);
+#ifdef DEBUG
+static void dump_passwd(struct passwd *);
+#endif
+
+static int passwd_read_snapshot_func(struct passwd *, char *);
+
+static int passwd_check_ambiguity(struct passwd_test_data *, struct passwd *);
+static int passwd_fill_test_data(struct passwd_test_data *,
+ int (*cb)(struct passwd *, void *));
+static int passwd_test_correctness(struct passwd *, void *);
+static int passwd_test_getpwnam(struct passwd *, void *);
+static int passwd_test_getpwuid(struct passwd *, void *);
+static int passwd_test_getpwent(struct passwd *, void *);
+
+IMPLEMENT_TEST_DATA(passwd)
+IMPLEMENT_TEST_FILE_SNAPSHOT(passwd)
+IMPLEMENT_1PASS_TEST(passwd)
+IMPLEMENT_2PASS_TEST(passwd)
+
+static void
+clone_passwd(struct passwd *dest, struct passwd const *src)
+{
+ ATF_REQUIRE(dest != NULL);
+ ATF_REQUIRE(src != NULL);
+
+ memcpy(dest, src, sizeof(struct passwd));
+ if (src->pw_name != NULL)
+ dest->pw_name = strdup(src->pw_name);
+ if (src->pw_passwd != NULL)
+ dest->pw_passwd = strdup(src->pw_passwd);
+ if (src->pw_class != NULL)
+ dest->pw_class = strdup(src->pw_class);
+ if (src->pw_gecos != NULL)
+ dest->pw_gecos = strdup(src->pw_gecos);
+ if (src->pw_dir != NULL)
+ dest->pw_dir = strdup(src->pw_dir);
+ if (src->pw_shell != NULL)
+ dest->pw_shell = strdup(dest->pw_shell);
+}
+
+static int
+compare_passwd(struct passwd *pwd1, struct passwd *pwd2, void *mdata __unused)
+{
+ ATF_REQUIRE(pwd1 != NULL);
+ ATF_REQUIRE(pwd2 != NULL);
+
+ if (pwd1 == pwd2)
+ return (0);
+
+ if (pwd1->pw_uid != pwd2->pw_uid ||
+ pwd1->pw_gid != pwd2->pw_gid ||
+ pwd1->pw_change != pwd2->pw_change ||
+ pwd1->pw_expire != pwd2->pw_expire ||
+ pwd1->pw_fields != pwd2->pw_fields ||
+ strcmp(pwd1->pw_name, pwd2->pw_name) != 0 ||
+ strcmp(pwd1->pw_passwd, pwd2->pw_passwd) != 0 ||
+ strcmp(pwd1->pw_class, pwd2->pw_class) != 0 ||
+ strcmp(pwd1->pw_gecos, pwd2->pw_gecos) != 0 ||
+ strcmp(pwd1->pw_dir, pwd2->pw_dir) != 0 ||
+ strcmp(pwd1->pw_shell, pwd2->pw_shell) != 0)
+ return (-1);
+ else
+ return (0);
+}
+
+static void
+free_passwd(struct passwd *pwd)
+{
+ free(pwd->pw_name);
+ free(pwd->pw_passwd);
+ free(pwd->pw_class);
+ free(pwd->pw_gecos);
+ free(pwd->pw_dir);
+ free(pwd->pw_shell);
+}
+
+static void
+sdump_passwd(struct passwd *pwd, char *buffer, size_t buflen)
+{
+ snprintf(buffer, buflen, "%s:%s:%d:%d:%jd:%s:%s:%s:%s:%jd:%d",
+ pwd->pw_name, pwd->pw_passwd, pwd->pw_uid, pwd->pw_gid,
+ (uintmax_t)pwd->pw_change, pwd->pw_class, pwd->pw_gecos,
+ pwd->pw_dir, pwd->pw_shell, (uintmax_t)pwd->pw_expire,
+ pwd->pw_fields);
+}
+
+#ifdef DEBUG
+static void
+dump_passwd(struct passwd *pwd)
+{
+ if (pwd != NULL) {
+ char buffer[2048];
+ sdump_passwd(pwd, buffer, sizeof(buffer));
+ printf("%s\n", buffer);
+ } else
+ printf("(null)\n");
+}
+#endif
+
+static int
+passwd_read_snapshot_func(struct passwd *pwd, char *line)
+{
+ char *s, *ps, *ts;
+ int i;
+
+#ifdef DEBUG
+ printf("1 line read from snapshot:\n%s\n", line);
+#endif
+
+ i = 0;
+ ps = line;
+ memset(pwd, 0, sizeof(struct passwd));
+ while ((s = strsep(&ps, ":")) != NULL) {
+ switch (i) {
+ case 0:
+ pwd->pw_name = strdup(s);
+ ATF_REQUIRE(pwd->pw_name != NULL);
+ break;
+ case 1:
+ pwd->pw_passwd = strdup(s);
+ ATF_REQUIRE(pwd->pw_passwd != NULL);
+ break;
+ case 2:
+ pwd->pw_uid = (uid_t)strtol(s, &ts, 10);
+ if (*ts != '\0')
+ goto fin;
+ break;
+ case 3:
+ pwd->pw_gid = (gid_t)strtol(s, &ts, 10);
+ if (*ts != '\0')
+ goto fin;
+ break;
+ case 4:
+ pwd->pw_change = (time_t)strtol(s, &ts, 10);
+ if (*ts != '\0')
+ goto fin;
+ break;
+ case 5:
+ pwd->pw_class = strdup(s);
+ ATF_REQUIRE(pwd->pw_class != NULL);
+ break;
+ case 6:
+ pwd->pw_gecos = strdup(s);
+ ATF_REQUIRE(pwd->pw_gecos != NULL);
+ break;
+ case 7:
+ pwd->pw_dir = strdup(s);
+ ATF_REQUIRE(pwd->pw_dir != NULL);
+ break;
+ case 8:
+ pwd->pw_shell = strdup(s);
+ ATF_REQUIRE(pwd->pw_shell != NULL);
+ break;
+ case 9:
+ pwd->pw_expire = (time_t)strtol(s, &ts, 10);
+ if (*ts != '\0')
+ goto fin;
+ break;
+ case 10:
+ pwd->pw_fields = (int)strtol(s, &ts, 10);
+ if (*ts != '\0')
+ goto fin;
+ break;
+ default:
+ break;
+ }
+ ++i;
+ }
+
+fin:
+ if (i != 11) {
+ free_passwd(pwd);
+ memset(pwd, 0, sizeof(struct passwd));
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+passwd_fill_test_data(struct passwd_test_data *td,
+ int (*cb)(struct passwd *, void *))
+{
+ struct passwd *pwd;
+ const int limit = 1024;
+ int count = 0;
+
+ setpassent(1);
+ while ((pwd = getpwent()) != NULL) {
+ if (passwd_test_correctness(pwd, NULL) == 0) {
+ TEST_DATA_APPEND(passwd, td, pwd);
+ if (cb != NULL && cb(pwd, td) != 0)
+ return (-1);
+ } else {
+ return (-1);
+ }
+ if (++count >= limit)
+ break;
+ }
+ endpwent();
+
+ return (0);
+}
+
+static int
+passwd_test_correctness(struct passwd *pwd, void *mdata __unused)
+{
+
+#ifdef DEBUG
+ printf("testing correctness with the following data:\n");
+ dump_passwd(pwd);
+#endif
+
+ if (pwd == NULL)
+ return (-1);
+
+ if (pwd->pw_name == NULL)
+ goto errfin;
+
+ if (pwd->pw_passwd == NULL)
+ goto errfin;
+
+ if (pwd->pw_class == NULL)
+ goto errfin;
+
+ if (pwd->pw_gecos == NULL)
+ goto errfin;
+
+ if (pwd->pw_dir == NULL)
+ goto errfin;
+
+ if (pwd->pw_shell == NULL)
+ goto errfin;
+
+#ifdef DEBUG
+ printf("correct\n");
+#endif
+
+ return (0);
+errfin:
+#ifdef DEBUG
+ printf("incorrect\n");
+#endif
+
+ return (-1);
+}
+
+/* passwd_check_ambiguity() is needed here because when doing the getpwent()
+ * calls sequence, records from different nsswitch sources can be different,
+ * though having the same pw_name/pw_uid */
+static int
+passwd_check_ambiguity(struct passwd_test_data *td, struct passwd *pwd)
+{
+
+ return (TEST_DATA_FIND(passwd, td, pwd, compare_passwd, NULL) !=
+ NULL ? 0 : -1);
+}
+
+static int
+passwd_test_getpwnam(struct passwd *pwd_model, void *mdata)
+{
+ struct passwd *pwd;
+
+#ifdef DEBUG
+ printf("testing getpwnam() with the following data:\n");
+ dump_passwd(pwd_model);
+#endif
+
+ pwd = getpwnam(pwd_model->pw_name);
+ if (passwd_test_correctness(pwd, NULL) != 0)
+ goto errfin;
+
+ if (compare_passwd(pwd, pwd_model, NULL) != 0 &&
+ passwd_check_ambiguity((struct passwd_test_data *)mdata, pwd) != 0)
+ goto errfin;
+
+#ifdef DEBUG
+ printf("ok\n");
+#endif
+ return (0);
+
+errfin:
+#ifdef DEBUG
+ printf("not ok\n");
+#endif
+ return (-1);
+}
+
+static int
+passwd_test_getpwuid(struct passwd *pwd_model, void *mdata)
+{
+ struct passwd *pwd;
+
+#ifdef DEBUG
+ printf("testing getpwuid() with the following data...\n");
+ dump_passwd(pwd_model);
+#endif
+
+ pwd = getpwuid(pwd_model->pw_uid);
+ if (passwd_test_correctness(pwd, NULL) != 0 ||
+ (compare_passwd(pwd, pwd_model, NULL) != 0 &&
+ passwd_check_ambiguity((struct passwd_test_data *)mdata,
+ pwd) != 0)) {
+#ifdef DEBUG
+ printf("not ok\n");
+#endif
+ return (-1);
+ } else {
+#ifdef DEBUG
+ printf("ok\n");
+#endif
+ return (0);
+ }
+}
+
+static int
+passwd_test_getpwent(struct passwd *pwd, void *mdata __unused)
+{
+ /*
+ * Only correctness can be checked when doing 1-pass test for
+ * getpwent().
+ */
+ return (passwd_test_correctness(pwd, NULL));
+}
+
+static int
+run_tests(const char *snapshot_file, enum test_methods method)
+{
+ struct passwd_test_data td, td_snap, td_2pass, td_interleaved;
+ int rv;
+
+ TEST_DATA_INIT(passwd, &td, clone_passwd, free_passwd);
+ TEST_DATA_INIT(passwd, &td_snap, clone_passwd, free_passwd);
+ if (snapshot_file != NULL) {
+ if (access(snapshot_file, W_OK | R_OK) != 0) {
+ if (errno == ENOENT)
+ method = TEST_BUILD_SNAPSHOT;
+ else {
+ printf("can't access the file %s\n",
+ snapshot_file);
+ rv = -1;
+ goto fin;
+ }
+ } else {
+ if (method == TEST_BUILD_SNAPSHOT) {
+ rv = 0;
+ goto fin;
+ }
+
+ TEST_SNAPSHOT_FILE_READ(passwd, snapshot_file,
+ &td_snap, passwd_read_snapshot_func);
+ }
+ }
+
+ rv = passwd_fill_test_data(&td, NULL);
+ if (rv == -1)
+ return (-1);
+
+ switch (method) {
+ case TEST_GETPWNAM:
+ if (snapshot_file == NULL)
+ rv = DO_1PASS_TEST(passwd, &td,
+ passwd_test_getpwnam, (void *)&td);
+ else
+ rv = DO_1PASS_TEST(passwd, &td_snap,
+ passwd_test_getpwnam, (void *)&td_snap);
+ break;
+ case TEST_GETPWUID:
+ if (snapshot_file == NULL)
+ rv = DO_1PASS_TEST(passwd, &td,
+ passwd_test_getpwuid, (void *)&td);
+ else
+ rv = DO_1PASS_TEST(passwd, &td_snap,
+ passwd_test_getpwuid, (void *)&td_snap);
+ break;
+ case TEST_GETPWENT:
+ if (snapshot_file == NULL)
+ rv = DO_1PASS_TEST(passwd, &td, passwd_test_getpwent,
+ (void *)&td);
+ else
+ rv = DO_2PASS_TEST(passwd, &td, &td_snap,
+ compare_passwd, NULL);
+ break;
+ case TEST_GETPWENT_2PASS:
+ TEST_DATA_INIT(passwd, &td_2pass, clone_passwd, free_passwd);
+ rv = passwd_fill_test_data(&td_2pass, NULL);
+ if (rv != -1)
+ rv = DO_2PASS_TEST(passwd, &td, &td_2pass,
+ compare_passwd, NULL);
+ TEST_DATA_DESTROY(passwd, &td_2pass);
+ break;
+ case TEST_GETPWENT_INTERLEAVED_GETPWNAM:
+ TEST_DATA_INIT(passwd, &td_interleaved, clone_passwd, free_passwd);
+ rv = passwd_fill_test_data(&td_interleaved, passwd_test_getpwnam);
+ if (rv != -1)
+ rv = DO_2PASS_TEST(passwd, &td, &td_interleaved,
+ compare_passwd, NULL);
+ TEST_DATA_DESTROY(passwd, &td_interleaved);
+ break;
+ case TEST_GETPWENT_INTERLEAVED_GETPWUID:
+ TEST_DATA_INIT(passwd, &td_interleaved, clone_passwd, free_passwd);
+ rv = passwd_fill_test_data(&td_interleaved, passwd_test_getpwuid);
+ if (rv != -1)
+ rv = DO_2PASS_TEST(passwd, &td, &td_interleaved,
+ compare_passwd, NULL);
+ TEST_DATA_DESTROY(passwd, &td_interleaved);
+ break;
+ case TEST_BUILD_SNAPSHOT:
+ if (snapshot_file != NULL)
+ rv = TEST_SNAPSHOT_FILE_WRITE(passwd, snapshot_file,
+ &td, sdump_passwd);
+ break;
+ default:
+ rv = 0;
+ break;
+ }
+
+fin:
+ TEST_DATA_DESTROY(passwd, &td_snap);
+ TEST_DATA_DESTROY(passwd, &td);
+
+ return (rv);
+}
+
+#define SNAPSHOT_FILE "snapshot_pwd"
+
+ATF_TC_WITHOUT_HEAD(getpwent);
+ATF_TC_BODY(getpwent, tc)
+{
+ ATF_REQUIRE(run_tests(NULL, TEST_GETPWENT) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getpwent_with_snapshot);
+ATF_TC_BODY(getpwent_with_snapshot, tc)
+{
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETPWENT) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getpwent_with_two_pass);
+ATF_TC_BODY(getpwent_with_two_pass, tc)
+{
+ ATF_REQUIRE(run_tests(NULL, TEST_GETPWENT_2PASS) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getpwnam);
+ATF_TC_BODY(getpwnam, tc)
+{
+ ATF_REQUIRE(run_tests(NULL, TEST_GETPWNAM) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getpwnam_with_snapshot);
+ATF_TC_BODY(getpwnam_with_snapshot, tc)
+{
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETPWNAM) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getpwuid);
+ATF_TC_BODY(getpwuid, tc)
+{
+ ATF_REQUIRE(run_tests(NULL, TEST_GETPWUID) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getpwuid_with_snapshot);
+ATF_TC_BODY(getpwuid_with_snapshot, tc)
+{
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETPWUID) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getpwent_interleaved_getpwnam);
+ATF_TC_BODY(getpwent_interleaved_getpwnam, tc)
+{
+ ATF_REQUIRE(run_tests(NULL, TEST_GETPWENT_INTERLEAVED_GETPWNAM) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getpwent_interleaved_getpwuid);
+ATF_TC_BODY(getpwent_interleaved_getpwuid, tc)
+{
+ ATF_REQUIRE(run_tests(NULL, TEST_GETPWENT_INTERLEAVED_GETPWUID) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, getpwent);
+ ATF_TP_ADD_TC(tp, getpwent_with_snapshot);
+ ATF_TP_ADD_TC(tp, getpwent_with_two_pass);
+ ATF_TP_ADD_TC(tp, getpwnam);
+ ATF_TP_ADD_TC(tp, getpwnam_with_snapshot);
+ ATF_TP_ADD_TC(tp, getpwuid);
+ ATF_TP_ADD_TC(tp, getpwuid_with_snapshot);
+ ATF_TP_ADD_TC(tp, getpwent_interleaved_getpwnam);
+ ATF_TP_ADD_TC(tp, getpwent_interleaved_getpwuid);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/nss/getrpc_test.c b/lib/libc/tests/nss/getrpc_test.c
new file mode 100644
index 000000000000..6cca3cab9e86
--- /dev/null
+++ b/lib/libc/tests/nss/getrpc_test.c
@@ -0,0 +1,555 @@
+/*-
+ * Copyright (c) 2006 Michael Bushkov <bushman@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <arpa/inet.h>
+#include <rpc/rpc.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stringlist.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "testutil.h"
+
+enum test_methods {
+ TEST_GETRPCENT,
+ TEST_GETRPCBYNAME,
+ TEST_GETRPCBYNUMBER,
+ TEST_GETRPCENT_2PASS,
+ TEST_BUILD_SNAPSHOT
+};
+
+DECLARE_TEST_DATA(rpcent)
+DECLARE_TEST_FILE_SNAPSHOT(rpcent)
+DECLARE_1PASS_TEST(rpcent)
+DECLARE_2PASS_TEST(rpcent)
+
+static void clone_rpcent(struct rpcent *, struct rpcent const *);
+static int compare_rpcent(struct rpcent *, struct rpcent *, void *);
+static void dump_rpcent(struct rpcent *);
+static void free_rpcent(struct rpcent *);
+
+static void sdump_rpcent(struct rpcent *, char *, size_t);
+static int rpcent_read_snapshot_func(struct rpcent *, char *);
+
+static int rpcent_check_ambiguity(struct rpcent_test_data *,
+ struct rpcent *);
+static int rpcent_fill_test_data(struct rpcent_test_data *);
+static int rpcent_test_correctness(struct rpcent *, void *);
+static int rpcent_test_getrpcbyname(struct rpcent *, void *);
+static int rpcent_test_getrpcbynumber(struct rpcent *, void *);
+static int rpcent_test_getrpcent(struct rpcent *, void *);
+
+IMPLEMENT_TEST_DATA(rpcent)
+IMPLEMENT_TEST_FILE_SNAPSHOT(rpcent)
+IMPLEMENT_1PASS_TEST(rpcent)
+IMPLEMENT_2PASS_TEST(rpcent)
+
+static void
+clone_rpcent(struct rpcent *dest, struct rpcent const *src)
+{
+ ATF_REQUIRE(dest != NULL);
+ ATF_REQUIRE(src != NULL);
+
+ char **cp;
+ int aliases_num;
+
+ memset(dest, 0, sizeof(struct rpcent));
+
+ if (src->r_name != NULL) {
+ dest->r_name = strdup(src->r_name);
+ ATF_REQUIRE(dest->r_name != NULL);
+ }
+
+ dest->r_number = src->r_number;
+
+ if (src->r_aliases != NULL) {
+ aliases_num = 0;
+ for (cp = src->r_aliases; *cp; ++cp)
+ ++aliases_num;
+
+ dest->r_aliases = calloc(aliases_num + 1, sizeof(char *));
+ ATF_REQUIRE(dest->r_aliases != NULL);
+
+ for (cp = src->r_aliases; *cp; ++cp) {
+ dest->r_aliases[cp - src->r_aliases] = strdup(*cp);
+ ATF_REQUIRE(dest->r_aliases[cp - src->r_aliases] != NULL);
+ }
+ }
+}
+
+static void
+free_rpcent(struct rpcent *rpc)
+{
+ char **cp;
+
+ ATF_REQUIRE(rpc != NULL);
+
+ free(rpc->r_name);
+
+ for (cp = rpc->r_aliases; *cp; ++cp)
+ free(*cp);
+ free(rpc->r_aliases);
+}
+
+static int
+compare_rpcent(struct rpcent *rpc1, struct rpcent *rpc2, void *mdata)
+{
+ char **c1, **c2;
+
+ if (rpc1 == rpc2)
+ return 0;
+
+ if ((rpc1 == NULL) || (rpc2 == NULL))
+ goto errfin;
+
+ if ((strcmp(rpc1->r_name, rpc2->r_name) != 0) ||
+ (rpc1->r_number != rpc2->r_number))
+ goto errfin;
+
+ c1 = rpc1->r_aliases;
+ c2 = rpc2->r_aliases;
+
+ if ((rpc1->r_aliases == NULL) || (rpc2->r_aliases == NULL))
+ goto errfin;
+
+ for (;*c1 && *c2; ++c1, ++c2)
+ if (strcmp(*c1, *c2) != 0)
+ goto errfin;
+
+ if ((*c1 != NULL) || (*c2 != NULL))
+ goto errfin;
+
+ return 0;
+
+errfin:
+ if (mdata == NULL) {
+ printf("following structures are not equal:\n");
+ dump_rpcent(rpc1);
+ dump_rpcent(rpc2);
+ }
+
+ return (-1);
+}
+
+static void
+sdump_rpcent(struct rpcent *rpc, char *buffer, size_t buflen)
+{
+ char **cp;
+ int written;
+
+ written = snprintf(buffer, buflen, "%s %d",
+ rpc->r_name, rpc->r_number);
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+
+ if (rpc->r_aliases != NULL) {
+ if (*(rpc->r_aliases) != NULL) {
+ for (cp = rpc->r_aliases; *cp; ++cp) {
+ written = snprintf(buffer, buflen, " %s", *cp);
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+
+ if (buflen == 0)
+ return;
+ }
+ } else
+ snprintf(buffer, buflen, " noaliases");
+ } else
+ snprintf(buffer, buflen, " (null)");
+}
+
+static int
+rpcent_read_snapshot_func(struct rpcent *rpc, char *line)
+{
+ StringList *sl;
+ char *s, *ps, *ts;
+ int i;
+
+ printf("1 line read from snapshot:\n%s\n", line);
+
+ i = 0;
+ sl = NULL;
+ ps = line;
+ memset(rpc, 0, sizeof(struct rpcent));
+ while ((s = strsep(&ps, " ")) != NULL) {
+ switch (i) {
+ case 0:
+ rpc->r_name = strdup(s);
+ ATF_REQUIRE(rpc->r_name != NULL);
+ break;
+
+ case 1:
+ rpc->r_number = (int)strtol(s, &ts, 10);
+ if (*ts != '\0') {
+ free(rpc->r_name);
+ return (-1);
+ }
+ break;
+
+ default:
+ if (sl == NULL) {
+ if (strcmp(s, "(null)") == 0)
+ return (0);
+
+ sl = sl_init();
+ ATF_REQUIRE(sl != NULL);
+
+ if (strcmp(s, "noaliases") != 0) {
+ ts = strdup(s);
+ ATF_REQUIRE(ts != NULL);
+ sl_add(sl, ts);
+ }
+ } else {
+ ts = strdup(s);
+ ATF_REQUIRE(ts != NULL);
+ sl_add(sl, ts);
+ }
+ break;
+ }
+ i++;
+ }
+
+ if (i < 3) {
+ free(rpc->r_name);
+ memset(rpc, 0, sizeof(struct rpcent));
+ return (-1);
+ }
+
+ sl_add(sl, NULL);
+ rpc->r_aliases = sl->sl_str;
+
+ /* NOTE: is it a dirty hack or not? */
+ free(sl);
+ return (0);
+}
+
+static void
+dump_rpcent(struct rpcent *result)
+{
+ if (result != NULL) {
+ char buffer[1024];
+ sdump_rpcent(result, buffer, sizeof(buffer));
+ printf("%s\n", buffer);
+ } else
+ printf("(null)\n");
+}
+
+static int
+rpcent_fill_test_data(struct rpcent_test_data *td)
+{
+ struct rpcent *rpc;
+
+ setrpcent(1);
+ while ((rpc = getrpcent()) != NULL) {
+ if (rpcent_test_correctness(rpc, NULL) == 0)
+ TEST_DATA_APPEND(rpcent, td, rpc);
+ else
+ return (-1);
+ }
+ endrpcent();
+
+ return (0);
+}
+
+static int
+rpcent_test_correctness(struct rpcent *rpc, void *mdata __unused)
+{
+
+ printf("testing correctness with the following data:\n");
+ dump_rpcent(rpc);
+
+ if (rpc == NULL)
+ goto errfin;
+
+ if (rpc->r_name == NULL)
+ goto errfin;
+
+ if (rpc->r_number < 0)
+ goto errfin;
+
+ if (rpc->r_aliases == NULL)
+ goto errfin;
+
+ printf("correct\n");
+
+ return (0);
+errfin:
+ printf("incorrect\n");
+
+ return (-1);
+}
+
+/* rpcent_check_ambiguity() is needed when one port+rpc is associated with
+ * more than one piece (these cases are usually marked as PROBLEM in
+ * /etc/peices. This functions is needed also when one piece+rpc is
+ * associated with several ports. We have to check all the rpcent structures
+ * to make sure that rpc really exists and correct */
+static int
+rpcent_check_ambiguity(struct rpcent_test_data *td, struct rpcent *rpc)
+{
+
+ return (TEST_DATA_FIND(rpcent, td, rpc, compare_rpcent,
+ NULL) != NULL ? 0 : -1);
+}
+
+static int
+rpcent_test_getrpcbyname(struct rpcent *rpc_model, void *mdata)
+{
+ char **alias;
+ struct rpcent *rpc;
+
+ printf("testing getrpcbyname() with the following data:\n");
+ dump_rpcent(rpc_model);
+
+ rpc = getrpcbyname(rpc_model->r_name);
+ if (rpcent_test_correctness(rpc, NULL) != 0)
+ goto errfin;
+
+ if ((compare_rpcent(rpc, rpc_model, NULL) != 0) &&
+ (rpcent_check_ambiguity((struct rpcent_test_data *)mdata, rpc)
+ !=0))
+ goto errfin;
+
+ for (alias = rpc_model->r_aliases; *alias; ++alias) {
+ rpc = getrpcbyname(*alias);
+
+ if (rpcent_test_correctness(rpc, NULL) != 0)
+ goto errfin;
+
+ if ((compare_rpcent(rpc, rpc_model, NULL) != 0) &&
+ (rpcent_check_ambiguity(
+ (struct rpcent_test_data *)mdata, rpc) != 0))
+ goto errfin;
+ }
+
+ printf("ok\n");
+ return (0);
+
+errfin:
+ printf("not ok\n");
+
+ return (-1);
+}
+
+static int
+rpcent_test_getrpcbynumber(struct rpcent *rpc_model, void *mdata)
+{
+ struct rpcent *rpc;
+
+ printf("testing getrpcbyport() with the following data...\n");
+ dump_rpcent(rpc_model);
+
+ rpc = getrpcbynumber(rpc_model->r_number);
+ if (rpcent_test_correctness(rpc, NULL) != 0 ||
+ (compare_rpcent(rpc, rpc_model, NULL) != 0 &&
+ rpcent_check_ambiguity((struct rpcent_test_data *)mdata, rpc)
+ != 0)) {
+ printf("not ok\n");
+ return (-1);
+ } else {
+ printf("ok\n");
+ return (0);
+ }
+}
+
+static int
+rpcent_test_getrpcent(struct rpcent *rpc, void *mdata __unused)
+{
+
+ /*
+ * Only correctness can be checked when doing 1-pass test for
+ * getrpcent().
+ */
+ return (rpcent_test_correctness(rpc, NULL));
+}
+
+static int
+run_tests(const char *snapshot_file, enum test_methods method)
+{
+ struct rpcent_test_data td, td_snap, td_2pass;
+ int rv;
+
+ TEST_DATA_INIT(rpcent, &td, clone_rpcent, free_rpcent);
+ TEST_DATA_INIT(rpcent, &td_snap, clone_rpcent, free_rpcent);
+ if (snapshot_file != NULL) {
+ if (access(snapshot_file, W_OK | R_OK) != 0) {
+ if (errno == ENOENT)
+ method = TEST_BUILD_SNAPSHOT;
+ else {
+ printf("can't access the file %s\n",
+ snapshot_file);
+
+ rv = -1;
+ goto fin;
+ }
+ } else {
+ if (method == TEST_BUILD_SNAPSHOT) {
+ rv = 0;
+ goto fin;
+ }
+
+ TEST_SNAPSHOT_FILE_READ(rpcent, snapshot_file,
+ &td_snap, rpcent_read_snapshot_func);
+ }
+ }
+
+ rv = rpcent_fill_test_data(&td);
+ if (rv == -1)
+ return (-1);
+ switch (method) {
+ case TEST_GETRPCBYNAME:
+ if (snapshot_file == NULL)
+ rv = DO_1PASS_TEST(rpcent, &td,
+ rpcent_test_getrpcbyname, (void *)&td);
+ else
+ rv = DO_1PASS_TEST(rpcent, &td_snap,
+ rpcent_test_getrpcbyname, (void *)&td_snap);
+ break;
+ case TEST_GETRPCBYNUMBER:
+ if (snapshot_file == NULL)
+ rv = DO_1PASS_TEST(rpcent, &td,
+ rpcent_test_getrpcbynumber, (void *)&td);
+ else
+ rv = DO_1PASS_TEST(rpcent, &td_snap,
+ rpcent_test_getrpcbynumber, (void *)&td_snap);
+ break;
+ case TEST_GETRPCENT:
+ if (snapshot_file == NULL)
+ rv = DO_1PASS_TEST(rpcent, &td, rpcent_test_getrpcent,
+ (void *)&td);
+ else
+ rv = DO_2PASS_TEST(rpcent, &td, &td_snap,
+ compare_rpcent, NULL);
+ break;
+ case TEST_GETRPCENT_2PASS:
+ TEST_DATA_INIT(rpcent, &td_2pass, clone_rpcent, free_rpcent);
+ rv = rpcent_fill_test_data(&td_2pass);
+ if (rv != -1)
+ rv = DO_2PASS_TEST(rpcent, &td, &td_2pass,
+ compare_rpcent, NULL);
+ TEST_DATA_DESTROY(rpcent, &td_2pass);
+ break;
+ case TEST_BUILD_SNAPSHOT:
+ if (snapshot_file != NULL)
+ rv = TEST_SNAPSHOT_FILE_WRITE(rpcent, snapshot_file, &td,
+ sdump_rpcent);
+ break;
+ default:
+ rv = 0;
+ break;
+ }
+
+fin:
+ TEST_DATA_DESTROY(rpcent, &td_snap);
+ TEST_DATA_DESTROY(rpcent, &td);
+
+ return (rv);
+}
+
+#define SNAPSHOT_FILE "snapshot_rpc"
+
+ATF_TC_WITHOUT_HEAD(build_snapshot);
+ATF_TC_BODY(build_snapshot, tc)
+{
+
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getrpcbyname);
+ATF_TC_BODY(getrpcbyname, tc)
+{
+
+ ATF_REQUIRE(run_tests(NULL, TEST_GETRPCBYNAME) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getrpcbyname_with_snapshot);
+ATF_TC_BODY(getrpcbyname_with_snapshot, tc)
+{
+
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETRPCBYNAME) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getrpcbynumber);
+ATF_TC_BODY(getrpcbynumber, tc)
+{
+
+ ATF_REQUIRE(run_tests(NULL, TEST_GETRPCBYNUMBER) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getrpcbynumber_with_snapshot);
+ATF_TC_BODY(getrpcbynumber_with_snapshot, tc)
+{
+
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETRPCBYNUMBER) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getrpcbyent);
+ATF_TC_BODY(getrpcbyent, tc)
+{
+
+ ATF_REQUIRE(run_tests(NULL, TEST_GETRPCENT) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getrpcbyent_with_snapshot);
+ATF_TC_BODY(getrpcbyent_with_snapshot, tc)
+{
+
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETRPCENT) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getrpcbyent_with_two_pass);
+ATF_TC_BODY(getrpcbyent_with_two_pass, tc)
+{
+
+ ATF_REQUIRE(run_tests(NULL, TEST_GETRPCENT_2PASS) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, build_snapshot);
+ ATF_TP_ADD_TC(tp, getrpcbyname);
+ ATF_TP_ADD_TC(tp, getrpcbyname_with_snapshot);
+ ATF_TP_ADD_TC(tp, getrpcbynumber);
+ ATF_TP_ADD_TC(tp, getrpcbynumber_with_snapshot);
+ ATF_TP_ADD_TC(tp, getrpcbyent);
+ ATF_TP_ADD_TC(tp, getrpcbyent_with_snapshot);
+ ATF_TP_ADD_TC(tp, getrpcbyent_with_two_pass);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/nss/getserv_test.c b/lib/libc/tests/nss/getserv_test.c
new file mode 100644
index 000000000000..cc66fdb2fa52
--- /dev/null
+++ b/lib/libc/tests/nss/getserv_test.c
@@ -0,0 +1,571 @@
+/*-
+ * Copyright (c) 2006 Michael Bushkov <bushman@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stringlist.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "testutil.h"
+
+enum test_methods {
+ TEST_GETSERVENT,
+ TEST_GETSERVBYNAME,
+ TEST_GETSERVBYPORT,
+ TEST_GETSERVENT_2PASS,
+ TEST_BUILD_SNAPSHOT
+};
+
+DECLARE_TEST_DATA(servent)
+DECLARE_TEST_FILE_SNAPSHOT(servent)
+DECLARE_1PASS_TEST(servent)
+DECLARE_2PASS_TEST(servent)
+
+static void clone_servent(struct servent *, struct servent const *);
+static int compare_servent(struct servent *, struct servent *, void *);
+static void dump_servent(struct servent *);
+static void free_servent(struct servent *);
+
+static void sdump_servent(struct servent *, char *, size_t);
+static int servent_read_snapshot_func(struct servent *, char *);
+
+static int servent_check_ambiguity(struct servent_test_data *,
+ struct servent *);
+static int servent_fill_test_data(struct servent_test_data *);
+static int servent_test_correctness(struct servent *, void *);
+static int servent_test_getservbyname(struct servent *, void *);
+static int servent_test_getservbyport(struct servent *, void *);
+static int servent_test_getservent(struct servent *, void *);
+
+IMPLEMENT_TEST_DATA(servent)
+IMPLEMENT_TEST_FILE_SNAPSHOT(servent)
+IMPLEMENT_1PASS_TEST(servent)
+IMPLEMENT_2PASS_TEST(servent)
+
+static void
+clone_servent(struct servent *dest, struct servent const *src)
+{
+ ATF_REQUIRE(dest != NULL);
+ ATF_REQUIRE(src != NULL);
+
+ char **cp;
+ int aliases_num;
+
+ memset(dest, 0, sizeof(struct servent));
+
+ if (src->s_name != NULL) {
+ dest->s_name = strdup(src->s_name);
+ ATF_REQUIRE(dest->s_name != NULL);
+ }
+
+ if (src->s_proto != NULL) {
+ dest->s_proto = strdup(src->s_proto);
+ ATF_REQUIRE(dest->s_proto != NULL);
+ }
+ dest->s_port = src->s_port;
+
+ if (src->s_aliases != NULL) {
+ aliases_num = 0;
+ for (cp = src->s_aliases; *cp; ++cp)
+ ++aliases_num;
+
+ dest->s_aliases = calloc(aliases_num + 1, sizeof(char *));
+ ATF_REQUIRE(dest->s_aliases != NULL);
+
+ for (cp = src->s_aliases; *cp; ++cp) {
+ dest->s_aliases[cp - src->s_aliases] = strdup(*cp);
+ ATF_REQUIRE(dest->s_aliases[cp - src->s_aliases] != NULL);
+ }
+ }
+}
+
+static void
+free_servent(struct servent *serv)
+{
+ char **cp;
+
+ ATF_REQUIRE(serv != NULL);
+
+ free(serv->s_name);
+ free(serv->s_proto);
+
+ for (cp = serv->s_aliases; *cp; ++cp)
+ free(*cp);
+ free(serv->s_aliases);
+}
+
+static int
+compare_servent(struct servent *serv1, struct servent *serv2, void *mdata)
+{
+ char **c1, **c2;
+
+ if (serv1 == serv2)
+ return 0;
+
+ if ((serv1 == NULL) || (serv2 == NULL))
+ goto errfin;
+
+ if ((strcmp(serv1->s_name, serv2->s_name) != 0) ||
+ (strcmp(serv1->s_proto, serv2->s_proto) != 0) ||
+ (serv1->s_port != serv2->s_port))
+ goto errfin;
+
+ c1 = serv1->s_aliases;
+ c2 = serv2->s_aliases;
+
+ if ((serv1->s_aliases == NULL) || (serv2->s_aliases == NULL))
+ goto errfin;
+
+ for (;*c1 && *c2; ++c1, ++c2)
+ if (strcmp(*c1, *c2) != 0)
+ goto errfin;
+
+ if ((*c1 != NULL) || (*c2 != NULL))
+ goto errfin;
+
+ return 0;
+
+errfin:
+ if (mdata == NULL) {
+ printf("following structures are not equal:\n");
+ dump_servent(serv1);
+ dump_servent(serv2);
+ }
+
+ return (-1);
+}
+
+static void
+sdump_servent(struct servent *serv, char *buffer, size_t buflen)
+{
+ char **cp;
+ int written;
+
+ written = snprintf(buffer, buflen, "%s %d %s",
+ serv->s_name, ntohs(serv->s_port), serv->s_proto);
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+
+ if (serv->s_aliases != NULL) {
+ if (*(serv->s_aliases) != NULL) {
+ for (cp = serv->s_aliases; *cp; ++cp) {
+ written = snprintf(buffer, buflen, " %s", *cp);
+ buffer += written;
+ if (written > (int)buflen)
+ return;
+ buflen -= written;
+
+ if (buflen == 0)
+ return;
+ }
+ } else
+ snprintf(buffer, buflen, " noaliases");
+ } else
+ snprintf(buffer, buflen, " (null)");
+}
+
+static int
+servent_read_snapshot_func(struct servent *serv, char *line)
+{
+ StringList *sl;
+ char *s, *ps, *ts;
+ int i;
+
+ printf("1 line read from snapshot:\n%s\n", line);
+
+ i = 0;
+ sl = NULL;
+ ps = line;
+ memset(serv, 0, sizeof(struct servent));
+ while ( (s = strsep(&ps, " ")) != NULL) {
+ switch (i) {
+ case 0:
+ serv->s_name = strdup(s);
+ ATF_REQUIRE(serv->s_name != NULL);
+ break;
+
+ case 1:
+ serv->s_port = htons(
+ (int)strtol(s, &ts, 10));
+ if (*ts != '\0') {
+ free(serv->s_name);
+ return (-1);
+ }
+ break;
+
+ case 2:
+ serv->s_proto = strdup(s);
+ ATF_REQUIRE(serv->s_proto != NULL);
+ break;
+
+ default:
+ if (sl == NULL) {
+ if (strcmp(s, "(null)") == 0)
+ return (0);
+
+ sl = sl_init();
+ ATF_REQUIRE(sl != NULL);
+
+ if (strcmp(s, "noaliases") != 0) {
+ ts = strdup(s);
+ ATF_REQUIRE(ts != NULL);
+ sl_add(sl, ts);
+ }
+ } else {
+ ts = strdup(s);
+ ATF_REQUIRE(ts != NULL);
+ sl_add(sl, ts);
+ }
+ break;
+ }
+ ++i;
+ }
+
+ if (i < 3) {
+ free(serv->s_name);
+ free(serv->s_proto);
+ memset(serv, 0, sizeof(struct servent));
+ return (-1);
+ }
+
+ sl_add(sl, NULL);
+ serv->s_aliases = sl->sl_str;
+
+ /* NOTE: is it a dirty hack or not? */
+ free(sl);
+ return (0);
+}
+
+static void
+dump_servent(struct servent *result)
+{
+ if (result != NULL) {
+ char buffer[1024];
+ sdump_servent(result, buffer, sizeof(buffer));
+ printf("%s\n", buffer);
+ } else
+ printf("(null)\n");
+}
+
+static int
+servent_fill_test_data(struct servent_test_data *td)
+{
+ struct servent *serv;
+ const int limit = 1024;
+ int count = 0;
+
+ setservent(1);
+ while ((serv = getservent()) != NULL) {
+ if (servent_test_correctness(serv, NULL) == 0)
+ TEST_DATA_APPEND(servent, td, serv);
+ else
+ return (-1);
+ if (++count >= limit)
+ break;
+ }
+ endservent();
+
+ return (0);
+}
+
+static int
+servent_test_correctness(struct servent *serv, void *mdata __unused)
+{
+ printf("testing correctness with the following data:\n");
+ dump_servent(serv);
+
+ if (serv == NULL)
+ goto errfin;
+
+ if (serv->s_name == NULL)
+ goto errfin;
+
+ if (serv->s_proto == NULL)
+ goto errfin;
+
+ if (ntohs(serv->s_port < 0))
+ goto errfin;
+
+ if (serv->s_aliases == NULL)
+ goto errfin;
+
+ printf("correct\n");
+
+ return (0);
+errfin:
+ printf("incorrect\n");
+
+ return (-1);
+}
+
+/* servent_check_ambiguity() is needed when one port+proto is associated with
+ * more than one service (these cases are usually marked as PROBLEM in
+ * /etc/services. This functions is needed also when one service+proto is
+ * associated with several ports. We have to check all the servent structures
+ * to make sure that serv really exists and correct */
+static int
+servent_check_ambiguity(struct servent_test_data *td, struct servent *serv)
+{
+
+ return (TEST_DATA_FIND(servent, td, serv, compare_servent,
+ NULL) != NULL ? 0 : -1);
+}
+
+static int
+servent_test_getservbyname(struct servent *serv_model, void *mdata)
+{
+ char **alias;
+ struct servent *serv;
+
+ printf("testing getservbyname() with the following data:\n");
+ dump_servent(serv_model);
+
+ serv = getservbyname(serv_model->s_name, serv_model->s_proto);
+ if (servent_test_correctness(serv, NULL) != 0)
+ goto errfin;
+
+ if ((compare_servent(serv, serv_model, NULL) != 0) &&
+ (servent_check_ambiguity((struct servent_test_data *)mdata, serv)
+ !=0))
+ goto errfin;
+
+ for (alias = serv_model->s_aliases; *alias; ++alias) {
+ serv = getservbyname(*alias, serv_model->s_proto);
+
+ if (servent_test_correctness(serv, NULL) != 0)
+ goto errfin;
+
+ if ((compare_servent(serv, serv_model, NULL) != 0) &&
+ (servent_check_ambiguity(
+ (struct servent_test_data *)mdata, serv) != 0))
+ goto errfin;
+ }
+
+ printf("ok\n");
+ return (0);
+
+errfin:
+ printf("not ok\n");
+
+ return (-1);
+}
+
+static int
+servent_test_getservbyport(struct servent *serv_model, void *mdata)
+{
+ struct servent *serv;
+
+ printf("testing getservbyport() with the following data...\n");
+ dump_servent(serv_model);
+
+ serv = getservbyport(serv_model->s_port, serv_model->s_proto);
+ if ((servent_test_correctness(serv, NULL) != 0) ||
+ ((compare_servent(serv, serv_model, NULL) != 0) &&
+ (servent_check_ambiguity((struct servent_test_data *)mdata, serv)
+ != 0))) {
+ printf("not ok\n");
+ return (-1);
+ } else {
+ printf("ok\n");
+ return (0);
+ }
+}
+
+static int
+servent_test_getservent(struct servent *serv, void *mdata __unused)
+{
+ /* Only correctness can be checked when doing 1-pass test for
+ * getservent(). */
+ return (servent_test_correctness(serv, NULL));
+}
+
+static int
+run_tests(const char *snapshot_file, enum test_methods method)
+{
+ struct servent_test_data td, td_snap, td_2pass;
+ int rv;
+
+ TEST_DATA_INIT(servent, &td, clone_servent, free_servent);
+ TEST_DATA_INIT(servent, &td_snap, clone_servent, free_servent);
+ if (snapshot_file != NULL) {
+ if (access(snapshot_file, W_OK | R_OK) != 0) {
+ if (errno == ENOENT)
+ method = TEST_BUILD_SNAPSHOT;
+ else {
+ printf("can't access the file %s\n",
+ snapshot_file);
+
+ rv = -1;
+ goto fin;
+ }
+ } else {
+ if (method == TEST_BUILD_SNAPSHOT) {
+ rv = 0;
+ goto fin;
+ }
+
+ TEST_SNAPSHOT_FILE_READ(servent, snapshot_file,
+ &td_snap, servent_read_snapshot_func);
+ }
+ }
+
+ rv = servent_fill_test_data(&td);
+ if (rv == -1)
+ return (-1);
+ switch (method) {
+ case TEST_GETSERVBYNAME:
+ if (snapshot_file == NULL)
+ rv = DO_1PASS_TEST(servent, &td,
+ servent_test_getservbyname, (void *)&td);
+ else
+ rv = DO_1PASS_TEST(servent, &td_snap,
+ servent_test_getservbyname, (void *)&td_snap);
+ break;
+ case TEST_GETSERVBYPORT:
+ if (snapshot_file == NULL)
+ rv = DO_1PASS_TEST(servent, &td,
+ servent_test_getservbyport, (void *)&td);
+ else
+ rv = DO_1PASS_TEST(servent, &td_snap,
+ servent_test_getservbyport, (void *)&td_snap);
+ break;
+ case TEST_GETSERVENT:
+ if (snapshot_file == NULL)
+ rv = DO_1PASS_TEST(servent, &td, servent_test_getservent,
+ (void *)&td);
+ else
+ rv = DO_2PASS_TEST(servent, &td, &td_snap,
+ compare_servent, NULL);
+ break;
+ case TEST_GETSERVENT_2PASS:
+ TEST_DATA_INIT(servent, &td_2pass, clone_servent, free_servent);
+ rv = servent_fill_test_data(&td_2pass);
+ if (rv != -1)
+ rv = DO_2PASS_TEST(servent, &td, &td_2pass,
+ compare_servent, NULL);
+ TEST_DATA_DESTROY(servent, &td_2pass);
+ break;
+ case TEST_BUILD_SNAPSHOT:
+ if (snapshot_file != NULL)
+ rv = TEST_SNAPSHOT_FILE_WRITE(servent, snapshot_file, &td,
+ sdump_servent);
+ break;
+ default:
+ rv = 0;
+ break;
+ }
+
+fin:
+ TEST_DATA_DESTROY(servent, &td_snap);
+ TEST_DATA_DESTROY(servent, &td);
+
+ return (rv);
+}
+
+#define SNAPSHOT_FILE "snapshot_serv"
+
+ATF_TC_WITHOUT_HEAD(build_snapshot);
+ATF_TC_BODY(build_snapshot, tc)
+{
+
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getservbyname);
+ATF_TC_BODY(getservbyname, tc)
+{
+
+ ATF_REQUIRE(run_tests(NULL, TEST_GETSERVBYNAME) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getservbyname_with_snapshot);
+ATF_TC_BODY(getservbyname_with_snapshot, tc)
+{
+
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETSERVBYNAME) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getservbyport);
+ATF_TC_BODY(getservbyport, tc)
+{
+
+ ATF_REQUIRE(run_tests(NULL, TEST_GETSERVBYPORT) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getservbyport_with_snapshot);
+ATF_TC_BODY(getservbyport_with_snapshot, tc)
+{
+
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETSERVBYPORT) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getservbyent);
+ATF_TC_BODY(getservbyent, tc)
+{
+
+ ATF_REQUIRE(run_tests(NULL, TEST_GETSERVENT) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getservbyent_with_snapshot);
+ATF_TC_BODY(getservbyent_with_snapshot, tc)
+{
+
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETSERVENT) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getservbyent_with_two_pass);
+ATF_TC_BODY(getservbyent_with_two_pass, tc)
+{
+
+ ATF_REQUIRE(run_tests(NULL, TEST_GETSERVENT_2PASS) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, build_snapshot);
+ ATF_TP_ADD_TC(tp, getservbyent);
+ ATF_TP_ADD_TC(tp, getservbyent_with_snapshot);
+ ATF_TP_ADD_TC(tp, getservbyent_with_two_pass);
+ ATF_TP_ADD_TC(tp, getservbyname);
+ ATF_TP_ADD_TC(tp, getservbyname_with_snapshot);
+ ATF_TP_ADD_TC(tp, getservbyport);
+ ATF_TP_ADD_TC(tp, getservbyport_with_snapshot);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/nss/getusershell_test.c b/lib/libc/tests/nss/getusershell_test.c
new file mode 100644
index 000000000000..ac23792fde8e
--- /dev/null
+++ b/lib/libc/tests/nss/getusershell_test.c
@@ -0,0 +1,221 @@
+/*-
+ * Copyright (c) 2006 Michael Bushkov <bushman@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "testutil.h"
+
+enum test_methods {
+ TEST_GETUSERSHELL,
+ TEST_BUILD_SNAPSHOT
+};
+
+struct usershell {
+ char *path;
+};
+
+DECLARE_TEST_DATA(usershell)
+DECLARE_TEST_FILE_SNAPSHOT(usershell)
+DECLARE_2PASS_TEST(usershell)
+
+static void clone_usershell(struct usershell *, struct usershell const *);
+static int compare_usershell(struct usershell *, struct usershell *, void *);
+static void free_usershell(struct usershell *);
+
+static void sdump_usershell(struct usershell *, char *, size_t);
+static void dump_usershell(struct usershell *);
+
+IMPLEMENT_TEST_DATA(usershell)
+IMPLEMENT_TEST_FILE_SNAPSHOT(usershell)
+IMPLEMENT_2PASS_TEST(usershell)
+
+static void
+clone_usershell(struct usershell *dest, struct usershell const *src)
+{
+ assert(dest != NULL);
+ assert(src != NULL);
+
+ if (src->path != NULL) {
+ dest->path = strdup(src->path);
+ assert(dest->path != NULL);
+ }
+}
+
+static int
+compare_usershell(struct usershell *us1, struct usershell *us2,
+ void *mdata __unused)
+{
+ int rv;
+
+ assert(us1 != NULL);
+ assert(us2 != NULL);
+
+ dump_usershell(us1);
+ dump_usershell(us2);
+
+ if (us1 == us2)
+ return (0);
+
+ rv = strcmp(us1->path, us2->path);
+ if (rv != 0) {
+ printf("following structures are not equal:\n");
+ dump_usershell(us1);
+ dump_usershell(us2);
+ }
+
+ return (rv);
+}
+
+static void
+free_usershell(struct usershell *us)
+{
+ free(us->path);
+}
+
+static void
+sdump_usershell(struct usershell *us, char *buffer, size_t buflen)
+{
+ snprintf(buffer, buflen, "%s", us->path);
+}
+
+static void
+dump_usershell(struct usershell *us)
+{
+ if (us != NULL) {
+ char buffer[2048];
+ sdump_usershell(us, buffer, sizeof(buffer));
+ printf("%s\n", buffer);
+ } else
+ printf("(null)\n");
+}
+
+static int
+usershell_read_snapshot_func(struct usershell *us, char *line)
+{
+
+ us->path = strdup(line);
+ ATF_REQUIRE(us->path != NULL);
+
+ return (0);
+}
+
+static int
+run_tests(const char *snapshot_file, enum test_methods method)
+{
+ struct usershell_test_data td, td_snap;
+ struct usershell ushell;
+ int rv;
+
+ rv = 0;
+
+ TEST_DATA_INIT(usershell, &td, clone_usershell, free_usershell);
+ TEST_DATA_INIT(usershell, &td_snap, clone_usershell, free_usershell);
+
+ setusershell();
+ while ((ushell.path = getusershell()) != NULL) {
+ printf("usershell found:\n");
+ dump_usershell(&ushell);
+ TEST_DATA_APPEND(usershell, &td, &ushell);
+ }
+ endusershell();
+
+ if (snapshot_file != NULL) {
+ if (access(snapshot_file, W_OK | R_OK) != 0) {
+ if (errno == ENOENT)
+ method = TEST_BUILD_SNAPSHOT;
+ else {
+ printf("can't access the snapshot file %s\n",
+ snapshot_file);
+
+ rv = -1;
+ goto fin;
+ }
+ } else {
+ rv = TEST_SNAPSHOT_FILE_READ(usershell, snapshot_file,
+ &td_snap, usershell_read_snapshot_func);
+ if (rv != 0) {
+ printf("error reading snapshot file\n");
+ goto fin;
+ }
+ }
+ }
+
+ switch (method) {
+ case TEST_GETUSERSHELL:
+ rv = DO_2PASS_TEST(usershell, &td, &td_snap,
+ compare_usershell, NULL);
+ break;
+ case TEST_BUILD_SNAPSHOT:
+ if (snapshot_file != NULL) {
+ rv = TEST_SNAPSHOT_FILE_WRITE(usershell, snapshot_file,
+ &td, sdump_usershell);
+ }
+ break;
+ default:
+ rv = 0;
+ break;
+ }
+
+fin:
+ TEST_DATA_DESTROY(usershell, &td_snap);
+ TEST_DATA_DESTROY(usershell, &td);
+
+ return (rv);
+}
+
+#define SNAPSHOT_FILE "snapshot_usershell"
+
+ATF_TC_WITHOUT_HEAD(getusershell_with_snapshot);
+ATF_TC_BODY(getusershell_with_snapshot, tc)
+{
+
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(getusershell_with_two_pass);
+ATF_TC_BODY(getusershell_with_two_pass, tc)
+{
+
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_BUILD_SNAPSHOT) == 0);
+ ATF_REQUIRE(run_tests(SNAPSHOT_FILE, TEST_GETUSERSHELL) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, getusershell_with_snapshot);
+ ATF_TP_ADD_TC(tp, getusershell_with_two_pass);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/nss/testutil.h b/lib/libc/tests/nss/testutil.h
new file mode 100644
index 000000000000..1f7b8086ec49
--- /dev/null
+++ b/lib/libc/tests/nss/testutil.h
@@ -0,0 +1,328 @@
+/*-
+ * Copyright (c) 2006 Michael Bushkov <bushman@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/queue.h>
+
+#define DECLARE_TEST_DATA(ent) \
+struct ent##_entry { \
+ struct ent data; \
+ STAILQ_ENTRY(ent##_entry) entries; \
+}; \
+ \
+struct ent##_test_data { \
+ void (*clone_func)(struct ent *, struct ent const *); \
+ void (*free_func)(struct ent *); \
+ \
+ STAILQ_HEAD(ent_head, ent##_entry) snapshot_data; \
+}; \
+ \
+void __##ent##_test_data_init(struct ent##_test_data *, \
+ void (*)(struct ent *, struct ent const *), \
+ void (*freef)(struct ent *)); \
+void __##ent##_test_data_destroy(struct ent##_test_data *); \
+ \
+void __##ent##_test_data_append(struct ent##_test_data *, struct ent *data);\
+int __##ent##_test_data_foreach(struct ent##_test_data *, \
+ int (*)(struct ent *, void *), void *); \
+int __##ent##_test_data_compare(struct ent##_test_data *, \
+ struct ent##_test_data *, int (*)(struct ent *, struct ent *, \
+ void *), void *); \
+struct ent *__##ent##_test_data_find(struct ent##_test_data *, struct ent *,\
+ int (*)(struct ent *, struct ent *, void *), void *); \
+void __##ent##_test_data_clear(struct ent##_test_data *);
+
+#define TEST_DATA_INIT(ent, td, clonef, freef)\
+ __##ent##_test_data_init(td, clonef, freef)
+#define TEST_DATA_DESTROY(ent, td) __##ent##_test_data_destroy(td)
+#define TEST_DATA_APPEND(ent, td, d) __##ent##_test_data_append(td, d)
+#define TEST_DATA_FOREACH(ent, td, f, mdata)\
+ __##ent##_test_data_foreach(td, f, mdata)
+#define TEST_DATA_COMPARE(ent, td1, td2, fcmp, mdata)\
+ __##ent##_test_data_compare(td1, td2, fcmp, mdata);
+#define TEST_DATA_FIND(ent, td, d, fcmp, mdata)\
+ __##ent##_test_data_find(td, d, fcmp, mdata)
+#define TEST_DATA_CLEAR(ent, td) __##ent##_test_data_clear(td)
+
+#define IMPLEMENT_TEST_DATA(ent) \
+void \
+__##ent##_test_data_init(struct ent##_test_data *td, \
+ void (*clonef)(struct ent *, struct ent const *), \
+ void (*freef)(struct ent *)) \
+{ \
+ ATF_REQUIRE(td != NULL); \
+ ATF_REQUIRE(clonef != NULL); \
+ ATF_REQUIRE(freef != NULL); \
+ \
+ memset(td, 0, sizeof(*td)); \
+ td->clone_func = clonef; \
+ td->free_func = freef; \
+ STAILQ_INIT(&td->snapshot_data); \
+} \
+ \
+void \
+__##ent##_test_data_destroy(struct ent##_test_data *td) \
+{ \
+ __##ent##_test_data_clear(td); \
+} \
+ \
+void \
+__##ent##_test_data_append(struct ent##_test_data *td, struct ent *app_data)\
+{ \
+ struct ent##_entry *e; \
+ \
+ ATF_REQUIRE(td != NULL); \
+ ATF_REQUIRE(app_data != NULL); \
+ \
+ e = (struct ent##_entry *)malloc(sizeof(struct ent##_entry)); \
+ ATF_REQUIRE(e != NULL); \
+ memset(e, 0, sizeof(struct ent##_entry)); \
+ \
+ td->clone_func(&e->data, app_data); \
+ STAILQ_INSERT_TAIL(&td->snapshot_data, e, entries); \
+} \
+ \
+int \
+__##ent##_test_data_foreach(struct ent##_test_data *td, \
+ int (*forf)(struct ent *, void *), void *mdata) \
+{ \
+ struct ent##_entry *e; \
+ int rv; \
+ \
+ ATF_REQUIRE(td != NULL); \
+ ATF_REQUIRE(forf != NULL); \
+ \
+ rv = 0; \
+ STAILQ_FOREACH(e, &td->snapshot_data, entries) { \
+ rv = forf(&e->data, mdata); \
+ if (rv != 0) \
+ break; \
+ } \
+ \
+ return (rv); \
+} \
+ \
+int \
+__##ent##_test_data_compare(struct ent##_test_data *td1, struct ent##_test_data *td2,\
+ int (*cmp_func)(struct ent *, struct ent *, void *), void *mdata)\
+{ \
+ struct ent##_entry *e1, *e2; \
+ int rv; \
+ \
+ ATF_REQUIRE(td1 != NULL); \
+ ATF_REQUIRE(td2 != NULL); \
+ ATF_REQUIRE(cmp_func != NULL); \
+ \
+ e1 = STAILQ_FIRST(&td1->snapshot_data); \
+ e2 = STAILQ_FIRST(&td2->snapshot_data); \
+ \
+ rv = 0; \
+ do { \
+ if ((e1 == NULL) || (e2 == NULL)) { \
+ if (e1 == e2) \
+ return (0); \
+ else \
+ return (-1); \
+ } \
+ \
+ rv = cmp_func(&e1->data, &e2->data, mdata); \
+ e1 = STAILQ_NEXT(e1, entries); \
+ e2 = STAILQ_NEXT(e2, entries); \
+ } while (rv == 0); \
+ \
+ return (rv); \
+} \
+ \
+struct ent * \
+__##ent##_test_data_find(struct ent##_test_data *td, struct ent *data, \
+ int (*cmp)(struct ent *, struct ent *, void *), void *mdata) \
+{ \
+ struct ent##_entry *e; \
+ struct ent *result; \
+ \
+ ATF_REQUIRE(td != NULL); \
+ ATF_REQUIRE(cmp != NULL); \
+ \
+ result = NULL; \
+ STAILQ_FOREACH(e, &td->snapshot_data, entries) { \
+ if (cmp(&e->data, data, mdata) == 0) { \
+ result = &e->data; \
+ break; \
+ } \
+ } \
+ \
+ return (result); \
+} \
+ \
+ \
+void \
+__##ent##_test_data_clear(struct ent##_test_data *td) \
+{ \
+ struct ent##_entry *e; \
+ ATF_REQUIRE(td != NULL); \
+ \
+ while (!STAILQ_EMPTY(&td->snapshot_data)) { \
+ e = STAILQ_FIRST(&td->snapshot_data); \
+ STAILQ_REMOVE_HEAD(&td->snapshot_data, entries); \
+ \
+ td->free_func(&e->data); \
+ free(e); \
+ e = NULL; \
+ } \
+}
+
+#define DECLARE_TEST_FILE_SNAPSHOT(ent) \
+struct ent##_snp_param { \
+ FILE *fp; \
+ void (*sdump_func)(struct ent *, char *, size_t); \
+}; \
+ \
+int __##ent##_snapshot_write_func(struct ent *, void *); \
+int __##ent##_snapshot_write(char const *, struct ent##_test_data *, \
+ void (*)(struct ent *, char *, size_t)); \
+int __##ent##_snapshot_read(char const *, struct ent##_test_data *, \
+ int (*)(struct ent *, char *));
+
+#define TEST_SNAPSHOT_FILE_WRITE(ent, fname, td, f) \
+ __##ent##_snapshot_write(fname, td, f)
+#define TEST_SNAPSHOT_FILE_READ(ent, fname, td, f) \
+ __##ent##_snapshot_read(fname, td, f)
+
+#define IMPLEMENT_TEST_FILE_SNAPSHOT(ent) \
+int \
+__##ent##_snapshot_write_func(struct ent *data, void *mdata) \
+{ \
+ char buffer[1024]; \
+ struct ent##_snp_param *param; \
+ \
+ ATF_REQUIRE(data != NULL); \
+ \
+ param = (struct ent##_snp_param *)mdata; \
+ param->sdump_func(data, buffer, sizeof(buffer)); \
+ fputs(buffer, param->fp); \
+ fputc('\n', param->fp); \
+ \
+ return (0); \
+} \
+ \
+int \
+__##ent##_snapshot_write(char const *fname, struct ent##_test_data *td, \
+ void (*sdump_func)(struct ent *, char *, size_t)) \
+{ \
+ struct ent##_snp_param param; \
+ \
+ ATF_REQUIRE(fname != NULL); \
+ ATF_REQUIRE(td != NULL); \
+ \
+ param.fp = fopen(fname, "w"); \
+ if (param.fp == NULL) \
+ return (-1); \
+ \
+ param.sdump_func = sdump_func; \
+ __##ent##_test_data_foreach(td, __##ent##_snapshot_write_func, &param);\
+ fclose(param.fp); \
+ \
+ return (0); \
+} \
+ \
+int \
+__##ent##_snapshot_read(char const *fname, struct ent##_test_data *td, \
+ int (*read_func)(struct ent *, char *)) \
+{ \
+ struct ent data; \
+ FILE *fi; \
+ size_t len; \
+ int rv; \
+ \
+ ATF_REQUIRE(fname != NULL); \
+ ATF_REQUIRE(td != NULL); \
+ \
+ fi = fopen(fname, "r"); \
+ if (fi == NULL) \
+ return (-1); \
+ \
+ rv = 0; \
+ while (!feof(fi)) { \
+ char *buf = fgetln(fi, &len); \
+ if (buf == NULL || len <= 1) \
+ continue; \
+ if (buf[len - 1] == '\n') \
+ buf[len - 1] = '\0'; \
+ else \
+ buf[len] = '\0'; \
+ if (buf[0] == '#') \
+ continue; \
+ rv = read_func(&data, buf); \
+ if (rv == 0) { \
+ __##ent##_test_data_append(td, &data); \
+ td->free_func(&data); \
+ } else \
+ goto fin; \
+ } \
+ \
+fin: \
+ fclose(fi); \
+ return (rv); \
+}
+
+#define DECLARE_1PASS_TEST(ent) \
+int __##ent##_1pass_test(struct ent##_test_data *, \
+ int (*)(struct ent *, void *), \
+ void *);
+
+#define DO_1PASS_TEST(ent, td, f, mdata) \
+ __##ent##_1pass_test(td, f, mdata)
+
+#define IMPLEMENT_1PASS_TEST(ent) \
+int \
+__##ent##_1pass_test(struct ent##_test_data *td, \
+ int (*tf)(struct ent *, void *), \
+ void *mdata) \
+{ \
+ int rv; \
+ rv = __##ent##_test_data_foreach(td, tf, mdata); \
+ \
+ return (rv); \
+}
+
+#define DECLARE_2PASS_TEST(ent) \
+int __##ent##_2pass_test(struct ent##_test_data *, \
+ struct ent##_test_data *, \
+ int (*)(struct ent *, struct ent *, void *), void *);
+
+#define DO_2PASS_TEST(ent, td1, td2, f, mdata) \
+ __##ent##_2pass_test(td1, td2, f, mdata)
+
+#define IMPLEMENT_2PASS_TEST(ent) \
+int \
+__##ent##_2pass_test(struct ent##_test_data *td1, \
+ struct ent##_test_data *td2, \
+ int (*cmp_func)(struct ent *, struct ent *, void *), \
+ void *cmp_mdata) \
+{ \
+ int rv; \
+ \
+ rv = __##ent##_test_data_compare(td1, td2, cmp_func, cmp_mdata);\
+ return (rv); \
+}
diff --git a/lib/libc/tests/regex/Makefile b/lib/libc/tests/regex/Makefile
new file mode 100644
index 000000000000..65675d94c59c
--- /dev/null
+++ b/lib/libc/tests/regex/Makefile
@@ -0,0 +1,8 @@
+PACKAGE= tests
+
+# local test cases
+ATF_TESTS_SH+= multibyte
+
+.include "Makefile.inc"
+.include "${.CURDIR:H}/Makefile.netbsd-tests"
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/regex/Makefile.depend b/lib/libc/tests/regex/Makefile.depend
new file mode 100644
index 000000000000..9df74fa6efd2
--- /dev/null
+++ b/lib/libc/tests/regex/Makefile.depend
@@ -0,0 +1,19 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libnetbsd \
+ lib/libutil \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/regex/Makefile.inc b/lib/libc/tests/regex/Makefile.inc
new file mode 100644
index 000000000000..aca7ae47be18
--- /dev/null
+++ b/lib/libc/tests/regex/Makefile.inc
@@ -0,0 +1,56 @@
+.include <bsd.own.mk>
+
+BINDIR?= ${TESTSDIR}
+WARNS?= 3
+
+# SKIP_LEFTASSOC -> these testcases fail on FreeBSD.
+IMPLEMENTATION?= -DREGEX_SPENCER -DSKIP_LEFTASSOC
+
+CFLAGS.h_regex+=-I${TESTSRC} -I${SRCTOP}/lib/libc/regex
+PROGS+= h_regex
+SRCS.h_regex= main.c split.c debug.c
+
+NETBSD_ATF_TESTS_SH= regex_test
+
+${PACKAGE}FILES+= README
+
+FILESGROUPS+= ${PACKAGE}DATA_FILES
+${PACKAGE}DATA_FILESPACKAGE=${PACKAGE}
+
+${PACKAGE}DATA_FILESDIR= ${TESTSDIR}/data
+${PACKAGE}DATA_FILES+= data/anchor.in
+${PACKAGE}DATA_FILES+= data/backref.in
+${PACKAGE}DATA_FILES+= data/basic.in
+${PACKAGE}DATA_FILES+= data/bracket.in
+${PACKAGE}DATA_FILES+= data/c_comments.in
+${PACKAGE}DATA_FILES+= data/complex.in
+${PACKAGE}DATA_FILES+= data/error.in
+${PACKAGE}DATA_FILES+= data/meta.in
+${PACKAGE}DATA_FILES+= data/nospec.in
+${PACKAGE}DATA_FILES+= data/paren.in
+${PACKAGE}DATA_FILES+= data/regress.in
+${PACKAGE}DATA_FILES+= data/repet_bounded.in
+${PACKAGE}DATA_FILES+= data/repet_multi.in
+${PACKAGE}DATA_FILES+= data/repet_ordinary.in
+${PACKAGE}DATA_FILES+= data/startend.in
+${PACKAGE}DATA_FILES+= data/subexp.in
+${PACKAGE}DATA_FILES+= data/subtle.in
+${PACKAGE}DATA_FILES+= data/word_bound.in
+${PACKAGE}DATA_FILES+= data/zero.in
+#${PACKAGE}DATA_FILES+= data/att/README
+${PACKAGE}DATA_FILES+= data/att/basic.dat
+${PACKAGE}DATA_FILES+= data/att/categorization.dat
+${PACKAGE}DATA_FILES+= data/att/forcedassoc.dat
+${PACKAGE}DATA_FILES+= data/att/leftassoc.dat
+${PACKAGE}DATA_FILES+= data/att/nullsubexpr.dat
+${PACKAGE}DATA_FILES+= data/att/repetition.dat
+${PACKAGE}DATA_FILES+= data/att/rightassoc.dat
+
+NETBSD_ATF_TESTS_C= exhaust_test
+NETBSD_ATF_TESTS_C+= regex_att_test
+
+.for t in ${NETBSD_ATF_TESTS_C}
+CFLAGS.$t+= -I${TESTSRC} ${IMPLEMENTATION}
+.endfor
+
+LIBADD.regex_att_test+= util
diff --git a/lib/libc/tests/regex/multibyte.sh b/lib/libc/tests/regex/multibyte.sh
new file mode 100755
index 000000000000..18323f500a2b
--- /dev/null
+++ b/lib/libc/tests/regex/multibyte.sh
@@ -0,0 +1,93 @@
+atf_test_case bmpat
+bmpat_head()
+{
+ atf_set "descr" "Check matching multibyte characters (PR153502)"
+}
+bmpat_body()
+{
+ export LC_CTYPE="C.UTF-8"
+
+ printf 'é' | atf_check -o "inline:é" \
+ sed -ne '/^.$/p'
+ printf 'éé' | atf_check -o "inline:éé" \
+ sed -ne '/^..$/p'
+ printf 'aéa' | atf_check -o "inline:aéa" \
+ sed -ne '/a.a/p'
+ printf 'aéa'| atf_check -o "inline:aéa" \
+ sed -ne '/a.*a/p'
+ printf 'aaéaa' | atf_check -o "inline:aaéaa" \
+ sed -ne '/aa.aa/p'
+ printf 'aéaéa' | atf_check -o "inline:aéaéa" \
+ sed -ne '/a.a.a/p'
+ printf 'éa' | atf_check -o "inline:éa" \
+ sed -ne '/.a/p'
+ printf 'aéaa' | atf_check -o "inline:aéaa" \
+ sed -ne '/a.aa/p'
+ printf 'éaé' | atf_check -o "inline:éaé" \
+ sed -ne '/.a./p'
+}
+
+atf_test_case icase
+icase_head()
+{
+ atf_set "descr" "Check case-insensitive matching for characters 128-255"
+}
+icase_body()
+{
+ export LC_CTYPE="C.UTF-8"
+
+ a=$(printf '\302\265\n') # U+00B5
+ b=$(printf '\316\234\n') # U+039C
+ c=$(printf '\316\274\n') # U+03BC
+
+ echo $b | atf_check -o "inline:$b\n" sed -ne "/$a/Ip"
+ echo $c | atf_check -o "inline:$c\n" sed -ne "/$a/Ip"
+}
+
+atf_test_case mbset cleanup
+mbset_head()
+{
+ atf_set "descr" "Check multibyte sets matching"
+}
+mbset_body()
+{
+ export LC_CTYPE="C.UTF-8"
+
+ # This involved an erroneously implemented optimization which reduces
+ # single-element sets to an exact match with a single codepoint.
+ # Match sets record small-codepoint characters in a bitmap and
+ # large-codepoint characters in an array; the optimization would falsely
+ # trigger if either the bitmap or the array was a singleton, ignoring
+ # the members of the other side of the set.
+ #
+ # To exercise this, we construct sets which have one member of one side
+ # and one or more of the other, and verify that all members can be
+ # found.
+ printf "a" > mbset; atf_check -o not-empty sed -ne '/[aà]/p' mbset
+ printf "à" > mbset; atf_check -o not-empty sed -ne '/[aà]/p' mbset
+ printf "a" > mbset; atf_check -o not-empty sed -ne '/[aàá]/p' mbset
+ printf "à" > mbset; atf_check -o not-empty sed -ne '/[aàá]/p' mbset
+ printf "á" > mbset; atf_check -o not-empty sed -ne '/[aàá]/p' mbset
+ printf "à" > mbset; atf_check -o not-empty sed -ne '/[abà]/p' mbset
+ printf "a" > mbset; atf_check -o not-empty sed -ne '/[abà]/p' mbset
+ printf "b" > mbset; atf_check -o not-empty sed -ne '/[abà]/p' mbset
+ printf "a" > mbset; atf_check -o not-empty sed -Ene '/[aà]/p' mbset
+ printf "à" > mbset; atf_check -o not-empty sed -Ene '/[aà]/p' mbset
+ printf "a" > mbset; atf_check -o not-empty sed -Ene '/[aàá]/p' mbset
+ printf "à" > mbset; atf_check -o not-empty sed -Ene '/[aàá]/p' mbset
+ printf "á" > mbset; atf_check -o not-empty sed -Ene '/[aàá]/p' mbset
+ printf "à" > mbset; atf_check -o not-empty sed -Ene '/[abà]/p' mbset
+ printf "a" > mbset; atf_check -o not-empty sed -Ene '/[abà]/p' mbset
+ printf "b" > mbset; atf_check -o not-empty sed -Ene '/[abà]/p' mbset
+}
+mbset_cleanup()
+{
+ rm -f mbset
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case bmpat
+ atf_add_test_case icase
+ atf_add_test_case mbset
+}
diff --git a/lib/libc/tests/resolv/Makefile b/lib/libc/tests/resolv/Makefile
new file mode 100644
index 000000000000..25c659d56685
--- /dev/null
+++ b/lib/libc/tests/resolv/Makefile
@@ -0,0 +1,13 @@
+PACKAGE= tests
+
+TESTSDIR= ${TESTSBASE}/lib/libc/resolv
+
+${PACKAGE}FILES+= mach
+
+ATF_TESTS_C+= resolv_test
+
+# Note: this test relies on being dynamically linked. You will get a
+# spurious PASS for a statically linked test.
+LIBADD.resolv_test+= pthread
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/resolv/Makefile.depend b/lib/libc/tests/resolv/Makefile.depend
new file mode 100644
index 000000000000..e14b2568ac65
--- /dev/null
+++ b/lib/libc/tests/resolv/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libthr \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/resolv/mach b/lib/libc/tests/resolv/mach
new file mode 100644
index 000000000000..df57df90e0a0
--- /dev/null
+++ b/lib/libc/tests/resolv/mach
@@ -0,0 +1,45 @@
+localhost
+anoncvs.cirr.com
+anoncvs.netbsd.se
+antioche.antioche.eu.org
+centaurus.4web.cz
+chur.math.ntnu.no
+console.netbsd.org
+cvs.netbsd.org
+cvsup.netbsd.se
+ftp.chg.ru
+ftp.estpak.ee
+ftp.fsn.hu
+ftp.funet.fi
+ftp.netbsd.org
+ftp.nluug.nl
+ftp.plig.org
+ftp.uni-erlangen.de
+ftp.xgate.co.kr
+gd.tuwien.ac.at
+gort.ludd.luth.se
+irc.warped.net
+knug.youn.co.kr
+mail.jp.netbsd.org
+mail.netbsd.org
+melanoma.cs.rmit.edu.au
+mirror.aarnet.edu.au
+moon.vub.ac.be
+net.bsd.cz
+netbsd.3miasto.net
+netbsd.4ka.mipt.ru
+netbsd.csie.nctu.edu.tw
+netbsd.enderunix.org
+netbsd.ftp.fu-berlin.de
+netbsd.pair.com
+netbsdiso.interoute.net.uk
+netbsdwww.cs.rmit.edu.au
+netbsdwww.interoute.net.uk
+ns.netbsd.org
+skeleton.phys.spbu.ru
+www.en.netbsd.de
+www.netbsd.cl
+www.netbsd.nl
+www.netbsd.org
+www.netbsd.ro
+zeppo.rediris.es
diff --git a/lib/libc/tests/resolv/resolv_test.c b/lib/libc/tests/resolv/resolv_test.c
new file mode 100644
index 000000000000..d7b836ed8f3e
--- /dev/null
+++ b/lib/libc/tests/resolv/resolv_test.c
@@ -0,0 +1,354 @@
+/* $NetBSD: resolv.c,v 1.6 2004/05/23 16:59:11 christos Exp $ */
+
+/*-
+ * Copyright (c) 2004 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 <sys/cdefs.h>
+__RCSID("$NetBSD: resolv.c,v 1.6 2004/05/23 16:59:11 christos Exp $");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdatomic.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <stringlist.h>
+
+#include <atf-c.h>
+
+#define NTHREADS 10
+#define NHOSTS 100
+#define WS " \t\n\r"
+
+enum method {
+ METHOD_GETADDRINFO,
+ METHOD_GETHOSTBY,
+ METHOD_GETIPNODEBY
+};
+
+static StringList *hosts = NULL;
+static _Atomic(int) *ask = NULL;
+static _Atomic(int) *got = NULL;
+static bool debug_output = 0;
+
+static void load(const char *);
+static void resolvone(long, int, enum method);
+static void *resolvloop(void *);
+static pthread_t run(int, enum method, long);
+
+#define DBG(...) do { \
+ if (debug_output) \
+ dprintf(STDOUT_FILENO, __VA_ARGS__); \
+ } while (0)
+
+static void
+load(const char *fname)
+{
+ FILE *fp;
+ size_t linecap;
+ char *line;
+
+ fp = fopen(fname, "r");
+ ATF_REQUIRE(fp != NULL);
+ line = NULL;
+ linecap = 0;
+ while (getline(&line, &linecap, fp) >= 0) {
+ char *ptr;
+
+ for (ptr = strtok(line, WS); ptr; ptr = strtok(NULL, WS)) {
+ if (ptr[0] == '#')
+ break;
+ sl_add(hosts, strdup(ptr));
+ }
+ }
+ free(line);
+
+ (void)fclose(fp);
+}
+
+static int
+resolv_getaddrinfo(long threadnum, char *host, int port, const char **errstr)
+{
+ char portstr[6], hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
+ struct addrinfo hints, *res;
+ int error;
+
+ snprintf(portstr, sizeof(portstr), "%d", port);
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(host, portstr, &hints, &res);
+ if (error == 0) {
+ DBG("T%ld: host %s ok\n", threadnum, host);
+ memset(hbuf, 0, sizeof(hbuf));
+ memset(pbuf, 0, sizeof(pbuf));
+ getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf),
+ pbuf, sizeof(pbuf), 0);
+ DBG("T%ld: reverse %s %s\n", threadnum, hbuf, pbuf);
+ freeaddrinfo(res);
+ } else {
+ *errstr = gai_strerror(error);
+ DBG("T%ld: host %s not found: %s\n", threadnum, host, *errstr);
+ }
+ return error;
+}
+
+static int
+resolv_gethostby(long threadnum, char *host, const char **errstr)
+{
+ char buf[1024];
+ struct hostent *hp, *hp2;
+
+ hp = gethostbyname(host);
+ if (hp) {
+ DBG("T%ld: host %s ok\n", threadnum, host);
+ memcpy(buf, hp->h_addr, hp->h_length);
+ hp2 = gethostbyaddr(buf, hp->h_length, hp->h_addrtype);
+ if (hp2) {
+ DBG("T%ld: reverse %s\n", threadnum, hp2->h_name);
+ }
+ } else {
+ *errstr = hstrerror(h_errno);
+ DBG("T%ld: host %s not found: %s\n", threadnum, host, *errstr);
+ }
+ return hp ? 0 : h_errno;
+}
+
+static int
+resolv_getipnodeby(long threadnum, char *host, const char **errstr)
+{
+ char buf[1024];
+ struct hostent *hp, *hp2;
+ int error = 0;
+
+ hp = getipnodebyname(host, AF_INET, 0, &error);
+ if (hp) {
+ DBG("T%ld: host %s ok\n", threadnum, host);
+ memcpy(buf, hp->h_addr, hp->h_length);
+ hp2 = getipnodebyaddr(buf, hp->h_length, hp->h_addrtype,
+ &error);
+ if (hp2) {
+ DBG("T%ld: reverse %s\n", threadnum, hp2->h_name);
+ freehostent(hp2);
+ }
+ freehostent(hp);
+ } else {
+ *errstr = hstrerror(error);
+ DBG("T%ld: host %s not found: %s\n", threadnum, host, *errstr);
+ }
+ return hp ? 0 : error;
+}
+
+static void
+resolvone(long threadnum, int n, enum method method)
+{
+ const char* errstr = NULL;
+ size_t i = (random() & 0x0fffffff) % hosts->sl_cur;
+ char *host = hosts->sl_str[i];
+ int error;
+
+ DBG("T%ld: %d resolving %s %zd\n", threadnum, n, host, i);
+ switch (method) {
+ case METHOD_GETADDRINFO:
+ error = resolv_getaddrinfo(threadnum, host, i, &errstr);
+ break;
+ case METHOD_GETHOSTBY:
+ error = resolv_gethostby(threadnum, host, &errstr);
+ break;
+ case METHOD_GETIPNODEBY:
+ error = resolv_getipnodeby(threadnum, host, &errstr);
+ break;
+ default:
+ /* UNREACHABLE */
+ /* XXX Needs an __assert_unreachable() for userland. */
+ assert(0 && "Unreachable segment reached");
+ abort();
+ break;
+ }
+ atomic_fetch_add_explicit(&ask[i], 1, memory_order_relaxed);
+ if (error == 0)
+ atomic_fetch_add_explicit(&got[i], 1, memory_order_relaxed);
+ else if (got[i] != 0)
+ fprintf(stderr,
+ "T%ld ERROR after previous success for %s: %d (%s)\n",
+ threadnum, hosts->sl_str[i], error, errstr);
+}
+
+struct resolvloop_args {
+ int nhosts;
+ enum method method;
+ long threadnum;
+};
+
+static void *
+resolvloop(void *p)
+{
+ struct resolvloop_args *args = p;
+ int nhosts = args->nhosts;
+
+ if (nhosts == 0) {
+ free(args);
+ return NULL;
+ }
+
+ do {
+ resolvone(args->threadnum, nhosts, args->method);
+ } while (--nhosts);
+ free(args);
+ return (void *)(uintptr_t)nhosts;
+}
+
+static pthread_t
+run(int nhosts, enum method method, long i)
+{
+ pthread_t t;
+ int rc;
+ struct resolvloop_args *args;
+
+ /* Created thread is responsible for free(). */
+ args = malloc(sizeof(*args));
+ ATF_REQUIRE(args != NULL);
+
+ args->nhosts = nhosts;
+ args->method = method;
+ args->threadnum = i + 1;
+ rc = pthread_create(&t, NULL, resolvloop, args);
+ ATF_REQUIRE_MSG(rc == 0, "pthread_create failed: %s", strerror(rc));
+ return t;
+}
+
+static int
+run_tests(const char *hostlist_file, enum method method)
+{
+ size_t nthreads = NTHREADS;
+ pthread_t *threads;
+ size_t nhosts = NHOSTS;
+ size_t i;
+ int c;
+ hosts = sl_init();
+
+ srandom(1234);
+ debug_output = getenv("DEBUG_OUTPUT") != NULL;
+
+ load(hostlist_file);
+
+ ATF_REQUIRE_MSG(0 < hosts->sl_cur, "0 hosts in %s", hostlist_file);
+
+ ask = calloc(hosts->sl_cur, sizeof(int));
+ ATF_REQUIRE(ask != NULL);
+
+ got = calloc(hosts->sl_cur, sizeof(int));
+ ATF_REQUIRE(got != NULL);
+
+ threads = calloc(nthreads, sizeof(pthread_t));
+ ATF_REQUIRE(threads != NULL);
+
+ for (i = 0; i < nthreads; i++) {
+ threads[i] = run(nhosts, method, i);
+ }
+ /* Wait for all threads to join and check that they checked all hosts */
+ for (i = 0; i < nthreads; i++) {
+ size_t remaining;
+
+ remaining = (uintptr_t)pthread_join(threads[i], NULL);
+ ATF_CHECK_EQ_MSG(0, remaining,
+ "Thread %zd still had %zd hosts to check!", i, remaining);
+ }
+
+ c = 0;
+ for (i = 0; i < hosts->sl_cur; i++) {
+ if (got[i] != 0) {
+ ATF_CHECK_EQ_MSG(ask[i], got[i],
+ "Error: host %s ask %d got %d", hosts->sl_str[i],
+ ask[i], got[i]);
+ c += ask[i] != got[i];
+ }
+ }
+ free(threads);
+ free(ask);
+ free(got);
+ sl_free(hosts, 1);
+ return c;
+}
+
+#define HOSTLIST_FILE "mach"
+
+#define RUN_TESTS(tc, method) \
+do { \
+ char *_hostlist_file; \
+ ATF_REQUIRE(0 < asprintf(&_hostlist_file, "%s/%s", \
+ atf_tc_get_config_var(tc, "srcdir"), HOSTLIST_FILE)); \
+ ATF_REQUIRE(run_tests(_hostlist_file, method) == 0); \
+} while(0)
+
+ATF_TC(getaddrinfo_test);
+ATF_TC_HEAD(getaddrinfo_test, tc) {
+ atf_tc_set_md_var(tc, "timeout", "1200");
+}
+ATF_TC_BODY(getaddrinfo_test, tc)
+{
+
+ RUN_TESTS(tc, METHOD_GETADDRINFO);
+}
+
+ATF_TC(gethostby_test);
+ATF_TC_HEAD(gethostby_test, tc) {
+ atf_tc_set_md_var(tc, "timeout", "1200");
+}
+ATF_TC_BODY(gethostby_test, tc)
+{
+
+ RUN_TESTS(tc, METHOD_GETHOSTBY);
+}
+
+ATF_TC(getipnodeby_test);
+ATF_TC_HEAD(getipnodeby_test, tc) {
+
+ atf_tc_set_md_var(tc, "timeout", "1200");
+}
+ATF_TC_BODY(getipnodeby_test, tc)
+{
+
+ RUN_TESTS(tc, METHOD_GETIPNODEBY);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, getaddrinfo_test);
+ ATF_TP_ADD_TC(tp, gethostby_test);
+ ATF_TP_ADD_TC(tp, getipnodeby_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/rpc/Makefile b/lib/libc/tests/rpc/Makefile
new file mode 100644
index 000000000000..621af96eb954
--- /dev/null
+++ b/lib/libc/tests/rpc/Makefile
@@ -0,0 +1,23 @@
+SRCS.xdr_test= ${RPCSRC:.x=_xdr.c} t_xdr.c ${RPCSRC:.x=.h} \
+ h_testbits.h
+
+NETBSD_ATF_TESTS_C= rpc_test
+NETBSD_ATF_TESTS_C+= xdr_test
+
+RPCSRC= h_testbits.x
+RPCGEN= RPCGEN_CPP=${CPP:Q} rpcgen -L -C
+
+h_testbits.h: ${RPCSRC}
+ ${RPCGEN} -h -o ${.TARGET} ${.ALLSRC}
+
+h_testbits_xdr.c: ${RPCSRC} h_testbits.h
+ ${RPCGEN} ${.ALLSRC:M*.x}
+
+CLEANFILES+= ${RPCSRC:.x=.h} ${RPCSRC:.x=.c} h_testbits_xdr.c
+CFLAGS+= -I${.OBJDIR}
+
+LIBADD+= rpcsvc util
+
+.include "../Makefile.netbsd-tests"
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/rpc/Makefile.depend b/lib/libc/tests/rpc/Makefile.depend
new file mode 100644
index 000000000000..50e2a6c3c778
--- /dev/null
+++ b/lib/libc/tests/rpc/Makefile.depend
@@ -0,0 +1,21 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/rpc \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libnetbsd \
+ lib/librpcsvc \
+ lib/libutil \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/secure/Makefile b/lib/libc/tests/secure/Makefile
new file mode 100644
index 000000000000..515f8f53a43e
--- /dev/null
+++ b/lib/libc/tests/secure/Makefile
@@ -0,0 +1,39 @@
+.include <bsd.own.mk>
+
+TESTSDIR:= ${TESTSBASE}/${RELDIR:C/libc\/tests/libc/}
+
+# sys/ headers
+FORTIFY_TCATS+= random
+FORTIFY_TCATS+= select
+FORTIFY_TCATS+= socket
+FORTIFY_TCATS+= uio
+
+# non-sys/ headers
+FORTIFY_TCATS+= poll
+FORTIFY_TCATS+= signal
+FORTIFY_TCATS+= stdlib
+FORTIFY_TCATS+= stdio
+FORTIFY_TCATS+= string
+FORTIFY_TCATS+= strings
+FORTIFY_TCATS+= unistd
+FORTIFY_TCATS+= wchar
+
+# Manually run after updating the test generator.
+lint-generator: .PHONY
+ @if ! which luacheck>/dev/null; then \
+ 1>&2 echo "devel/lua-luacheck is required to regenerate and lint these tests"; \
+ exit 1; \
+ fi
+ luacheck ${.CURDIR}/generate-fortify-tests.lua
+
+generate-tests: .PHONY lint-generator
+.for tcat in ${FORTIFY_TCATS}
+ATF_TESTS_C+= fortify_${tcat}_test
+
+.ORDER: lint-generator generate-tests-${tcat}
+generate-tests: generate-tests-${tcat}
+generate-tests-${tcat}: .PHONY
+ ${.CURDIR}/generate-fortify-tests.lua ${tcat} > ${.CURDIR}/fortify_${tcat}_test.c
+.endfor
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/secure/fortify_poll_test.c b/lib/libc/tests/secure/fortify_poll_test.c
new file mode 100644
index 000000000000..3810c16c122f
--- /dev/null
+++ b/lib/libc/tests/secure/fortify_poll_test.c
@@ -0,0 +1,617 @@
+/* @generated by `generate-fortify-tests.lua "poll"` */
+
+#define _FORTIFY_SOURCE 2
+#define TMPFILE_SIZE (1024 * 32)
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/random.h>
+#include <sys/resource.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <atf-c.h>
+
+static FILE * __unused
+new_fp(size_t __len)
+{
+ static char fpbuf[LINE_MAX];
+ FILE *fp;
+
+ ATF_REQUIRE(__len <= sizeof(fpbuf));
+
+ memset(fpbuf, 'A', sizeof(fpbuf) - 1);
+ fpbuf[sizeof(fpbuf) - 1] = '\0';
+
+ fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
+ ATF_REQUIRE(fp != NULL);
+
+ return (fp);
+}
+
+/*
+ * Create a new symlink to use for readlink(2) style tests, we'll just use a
+ * random target name to have something interesting to look at.
+ */
+static const char * __unused
+new_symlink(size_t __len)
+{
+ static const char linkname[] = "link";
+ char target[MAXNAMLEN];
+ int error;
+
+ ATF_REQUIRE(__len <= sizeof(target));
+
+ arc4random_buf(target, sizeof(target));
+
+ error = unlink(linkname);
+ ATF_REQUIRE(error == 0 || errno == ENOENT);
+
+ error = symlink(target, linkname);
+ ATF_REQUIRE(error == 0);
+
+ return (linkname);
+}
+
+/*
+ * For our purposes, first descriptor will be the reader; we'll send both
+ * raw data and a control message over it so that the result can be used for
+ * any of our recv*() tests.
+ */
+static void __unused
+new_socket(int sock[2])
+{
+ unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 };
+ static char sockbuf[256];
+ ssize_t rv;
+ size_t total = 0;
+ struct msghdr hdr = { 0 };
+ struct cmsghdr *cmsg;
+ int error, fd;
+
+ error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
+ ATF_REQUIRE(error == 0);
+
+ while (total != sizeof(sockbuf)) {
+ rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0);
+
+ ATF_REQUIRE_MSG(rv > 0,
+ "expected bytes sent, got %zd with %zu left (size %zu, total %zu)",
+ rv, sizeof(sockbuf) - total, sizeof(sockbuf), total);
+ ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf),
+ "%zd exceeds total %zu", rv, sizeof(sockbuf));
+ total += rv;
+ }
+
+ hdr.msg_control = ctrl;
+ hdr.msg_controllen = sizeof(ctrl);
+
+ cmsg = CMSG_FIRSTHDR(&hdr);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ fd = STDIN_FILENO;
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
+
+ error = sendmsg(sock[1], &hdr, 0);
+ ATF_REQUIRE(error != -1);
+}
+
+/*
+ * Constructs a tmpfile that we can use for testing read(2) and friends.
+ */
+static int __unused
+new_tmpfile(void)
+{
+ char buf[1024];
+ ssize_t rv;
+ size_t written;
+ int fd;
+
+ fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
+ ATF_REQUIRE(fd >= 0);
+
+ written = 0;
+ while (written < TMPFILE_SIZE) {
+ rv = write(fd, buf, sizeof(buf));
+ ATF_REQUIRE(rv > 0);
+
+ written += rv;
+ }
+
+ ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
+ return (fd);
+}
+
+static void
+disable_coredumps(void)
+{
+ struct rlimit rl = { 0 };
+
+ if (setrlimit(RLIMIT_CORE, &rl) == -1)
+ _exit(EX_OSERR);
+}
+
+/*
+ * Replaces stdin with a file that we can actually read from, for tests where
+ * we want a FILE * or fd that we can get data from.
+ */
+static void __unused
+replace_stdin(void)
+{
+ int fd;
+
+ fd = new_tmpfile();
+
+ (void)dup2(fd, STDIN_FILENO);
+ if (fd != STDIN_FILENO)
+ close(fd);
+}
+
+ATF_TC(poll_before_end);
+ATF_TC_HEAD(poll_before_end, tc)
+{
+}
+ATF_TC_BODY(poll_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct pollfd __buf[4];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 4 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {
+ __stack.__buf[i].fd = -1;
+ }
+
+ poll(__stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(poll_end);
+ATF_TC_HEAD(poll_end, tc)
+{
+}
+ATF_TC_BODY(poll_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct pollfd __buf[4];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 4;
+ const size_t __idx __unused = __len - 1;
+
+ for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {
+ __stack.__buf[i].fd = -1;
+ }
+
+ poll(__stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(poll_after_end);
+ATF_TC_HEAD(poll_after_end, tc)
+{
+}
+ATF_TC_BODY(poll_after_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct pollfd __buf[4];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 4 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {
+ __stack.__buf[i].fd = -1;
+ }
+
+ poll(__stack.__buf, __len, 0);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(poll_heap_before_end);
+ATF_TC_HEAD(poll_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(poll_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct pollfd * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (4);
+ const size_t __len = 4 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+ for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {
+ __stack.__buf[i].fd = -1;
+ }
+
+ poll(__stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(poll_heap_end);
+ATF_TC_HEAD(poll_heap_end, tc)
+{
+}
+ATF_TC_BODY(poll_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct pollfd * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (4);
+ const size_t __len = 4;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+ for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {
+ __stack.__buf[i].fd = -1;
+ }
+
+ poll(__stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(poll_heap_after_end);
+ATF_TC_HEAD(poll_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(poll_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct pollfd * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (4);
+ const size_t __len = 4 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {
+ __stack.__buf[i].fd = -1;
+ }
+
+ poll(__stack.__buf, __len, 0);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(ppoll_before_end);
+ATF_TC_HEAD(ppoll_before_end, tc)
+{
+}
+ATF_TC_BODY(ppoll_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct pollfd __buf[4];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 4 - 1;
+ const size_t __idx __unused = __len - 1;
+ struct timespec tv = { 0 };
+
+ for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {
+ __stack.__buf[i].fd = -1;
+ }
+
+ ppoll(__stack.__buf, __len, &tv, NULL);
+#undef BUF
+
+}
+
+ATF_TC(ppoll_end);
+ATF_TC_HEAD(ppoll_end, tc)
+{
+}
+ATF_TC_BODY(ppoll_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct pollfd __buf[4];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 4;
+ const size_t __idx __unused = __len - 1;
+ struct timespec tv = { 0 };
+
+ for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {
+ __stack.__buf[i].fd = -1;
+ }
+
+ ppoll(__stack.__buf, __len, &tv, NULL);
+#undef BUF
+
+}
+
+ATF_TC(ppoll_after_end);
+ATF_TC_HEAD(ppoll_after_end, tc)
+{
+}
+ATF_TC_BODY(ppoll_after_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct pollfd __buf[4];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 4 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ struct timespec tv = { 0 };
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {
+ __stack.__buf[i].fd = -1;
+ }
+
+ ppoll(__stack.__buf, __len, &tv, NULL);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(ppoll_heap_before_end);
+ATF_TC_HEAD(ppoll_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(ppoll_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct pollfd * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (4);
+ const size_t __len = 4 - 1;
+ const size_t __idx __unused = __len - 1;
+ struct timespec tv = { 0 };
+
+ __stack.__buf = malloc(__bufsz);
+ for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {
+ __stack.__buf[i].fd = -1;
+ }
+
+ ppoll(__stack.__buf, __len, &tv, NULL);
+#undef BUF
+
+}
+
+ATF_TC(ppoll_heap_end);
+ATF_TC_HEAD(ppoll_heap_end, tc)
+{
+}
+ATF_TC_BODY(ppoll_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct pollfd * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (4);
+ const size_t __len = 4;
+ const size_t __idx __unused = __len - 1;
+ struct timespec tv = { 0 };
+
+ __stack.__buf = malloc(__bufsz);
+ for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {
+ __stack.__buf[i].fd = -1;
+ }
+
+ ppoll(__stack.__buf, __len, &tv, NULL);
+#undef BUF
+
+}
+
+ATF_TC(ppoll_heap_after_end);
+ATF_TC_HEAD(ppoll_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(ppoll_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct pollfd * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (4);
+ const size_t __len = 4 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ struct timespec tv = { 0 };
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {
+ __stack.__buf[i].fd = -1;
+ }
+
+ ppoll(__stack.__buf, __len, &tv, NULL);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, poll_before_end);
+ ATF_TP_ADD_TC(tp, poll_end);
+ ATF_TP_ADD_TC(tp, poll_after_end);
+ ATF_TP_ADD_TC(tp, poll_heap_before_end);
+ ATF_TP_ADD_TC(tp, poll_heap_end);
+ ATF_TP_ADD_TC(tp, poll_heap_after_end);
+ ATF_TP_ADD_TC(tp, ppoll_before_end);
+ ATF_TP_ADD_TC(tp, ppoll_end);
+ ATF_TP_ADD_TC(tp, ppoll_after_end);
+ ATF_TP_ADD_TC(tp, ppoll_heap_before_end);
+ ATF_TP_ADD_TC(tp, ppoll_heap_end);
+ ATF_TP_ADD_TC(tp, ppoll_heap_after_end);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/secure/fortify_random_test.c b/lib/libc/tests/secure/fortify_random_test.c
new file mode 100644
index 000000000000..2f47c981b5ae
--- /dev/null
+++ b/lib/libc/tests/secure/fortify_random_test.c
@@ -0,0 +1,316 @@
+/* @generated by `generate-fortify-tests.lua "random"` */
+
+#define _FORTIFY_SOURCE 2
+#define TMPFILE_SIZE (1024 * 32)
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/random.h>
+#include <sys/resource.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <atf-c.h>
+
+static FILE * __unused
+new_fp(size_t __len)
+{
+ static char fpbuf[LINE_MAX];
+ FILE *fp;
+
+ ATF_REQUIRE(__len <= sizeof(fpbuf));
+
+ memset(fpbuf, 'A', sizeof(fpbuf) - 1);
+ fpbuf[sizeof(fpbuf) - 1] = '\0';
+
+ fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
+ ATF_REQUIRE(fp != NULL);
+
+ return (fp);
+}
+
+/*
+ * Create a new symlink to use for readlink(2) style tests, we'll just use a
+ * random target name to have something interesting to look at.
+ */
+static const char * __unused
+new_symlink(size_t __len)
+{
+ static const char linkname[] = "link";
+ char target[MAXNAMLEN];
+ int error;
+
+ ATF_REQUIRE(__len <= sizeof(target));
+
+ arc4random_buf(target, sizeof(target));
+
+ error = unlink(linkname);
+ ATF_REQUIRE(error == 0 || errno == ENOENT);
+
+ error = symlink(target, linkname);
+ ATF_REQUIRE(error == 0);
+
+ return (linkname);
+}
+
+/*
+ * For our purposes, first descriptor will be the reader; we'll send both
+ * raw data and a control message over it so that the result can be used for
+ * any of our recv*() tests.
+ */
+static void __unused
+new_socket(int sock[2])
+{
+ unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 };
+ static char sockbuf[256];
+ ssize_t rv;
+ size_t total = 0;
+ struct msghdr hdr = { 0 };
+ struct cmsghdr *cmsg;
+ int error, fd;
+
+ error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
+ ATF_REQUIRE(error == 0);
+
+ while (total != sizeof(sockbuf)) {
+ rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0);
+
+ ATF_REQUIRE_MSG(rv > 0,
+ "expected bytes sent, got %zd with %zu left (size %zu, total %zu)",
+ rv, sizeof(sockbuf) - total, sizeof(sockbuf), total);
+ ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf),
+ "%zd exceeds total %zu", rv, sizeof(sockbuf));
+ total += rv;
+ }
+
+ hdr.msg_control = ctrl;
+ hdr.msg_controllen = sizeof(ctrl);
+
+ cmsg = CMSG_FIRSTHDR(&hdr);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ fd = STDIN_FILENO;
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
+
+ error = sendmsg(sock[1], &hdr, 0);
+ ATF_REQUIRE(error != -1);
+}
+
+/*
+ * Constructs a tmpfile that we can use for testing read(2) and friends.
+ */
+static int __unused
+new_tmpfile(void)
+{
+ char buf[1024];
+ ssize_t rv;
+ size_t written;
+ int fd;
+
+ fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
+ ATF_REQUIRE(fd >= 0);
+
+ written = 0;
+ while (written < TMPFILE_SIZE) {
+ rv = write(fd, buf, sizeof(buf));
+ ATF_REQUIRE(rv > 0);
+
+ written += rv;
+ }
+
+ ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
+ return (fd);
+}
+
+static void
+disable_coredumps(void)
+{
+ struct rlimit rl = { 0 };
+
+ if (setrlimit(RLIMIT_CORE, &rl) == -1)
+ _exit(EX_OSERR);
+}
+
+/*
+ * Replaces stdin with a file that we can actually read from, for tests where
+ * we want a FILE * or fd that we can get data from.
+ */
+static void __unused
+replace_stdin(void)
+{
+ int fd;
+
+ fd = new_tmpfile();
+
+ (void)dup2(fd, STDIN_FILENO);
+ if (fd != STDIN_FILENO)
+ close(fd);
+}
+
+ATF_TC(getrandom_before_end);
+ATF_TC_HEAD(getrandom_before_end, tc)
+{
+}
+ATF_TC_BODY(getrandom_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ getrandom(__stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(getrandom_end);
+ATF_TC_HEAD(getrandom_end, tc)
+{
+}
+ATF_TC_BODY(getrandom_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ getrandom(__stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(getrandom_heap_before_end);
+ATF_TC_HEAD(getrandom_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(getrandom_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ getrandom(__stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(getrandom_heap_end);
+ATF_TC_HEAD(getrandom_heap_end, tc)
+{
+}
+ATF_TC_BODY(getrandom_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ getrandom(__stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(getrandom_heap_after_end);
+ATF_TC_HEAD(getrandom_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(getrandom_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ getrandom(__stack.__buf, __len, 0);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, getrandom_before_end);
+ ATF_TP_ADD_TC(tp, getrandom_end);
+ ATF_TP_ADD_TC(tp, getrandom_heap_before_end);
+ ATF_TP_ADD_TC(tp, getrandom_heap_end);
+ ATF_TP_ADD_TC(tp, getrandom_heap_after_end);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/secure/fortify_select_test.c b/lib/libc/tests/secure/fortify_select_test.c
new file mode 100644
index 000000000000..5ee97a352e2e
--- /dev/null
+++ b/lib/libc/tests/secure/fortify_select_test.c
@@ -0,0 +1,769 @@
+/* @generated by `generate-fortify-tests.lua "select"` */
+
+#define _FORTIFY_SOURCE 2
+#define TMPFILE_SIZE (1024 * 32)
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/random.h>
+#include <sys/resource.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <atf-c.h>
+
+static FILE * __unused
+new_fp(size_t __len)
+{
+ static char fpbuf[LINE_MAX];
+ FILE *fp;
+
+ ATF_REQUIRE(__len <= sizeof(fpbuf));
+
+ memset(fpbuf, 'A', sizeof(fpbuf) - 1);
+ fpbuf[sizeof(fpbuf) - 1] = '\0';
+
+ fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
+ ATF_REQUIRE(fp != NULL);
+
+ return (fp);
+}
+
+/*
+ * Create a new symlink to use for readlink(2) style tests, we'll just use a
+ * random target name to have something interesting to look at.
+ */
+static const char * __unused
+new_symlink(size_t __len)
+{
+ static const char linkname[] = "link";
+ char target[MAXNAMLEN];
+ int error;
+
+ ATF_REQUIRE(__len <= sizeof(target));
+
+ arc4random_buf(target, sizeof(target));
+
+ error = unlink(linkname);
+ ATF_REQUIRE(error == 0 || errno == ENOENT);
+
+ error = symlink(target, linkname);
+ ATF_REQUIRE(error == 0);
+
+ return (linkname);
+}
+
+/*
+ * For our purposes, first descriptor will be the reader; we'll send both
+ * raw data and a control message over it so that the result can be used for
+ * any of our recv*() tests.
+ */
+static void __unused
+new_socket(int sock[2])
+{
+ unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 };
+ static char sockbuf[256];
+ ssize_t rv;
+ size_t total = 0;
+ struct msghdr hdr = { 0 };
+ struct cmsghdr *cmsg;
+ int error, fd;
+
+ error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
+ ATF_REQUIRE(error == 0);
+
+ while (total != sizeof(sockbuf)) {
+ rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0);
+
+ ATF_REQUIRE_MSG(rv > 0,
+ "expected bytes sent, got %zd with %zu left (size %zu, total %zu)",
+ rv, sizeof(sockbuf) - total, sizeof(sockbuf), total);
+ ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf),
+ "%zd exceeds total %zu", rv, sizeof(sockbuf));
+ total += rv;
+ }
+
+ hdr.msg_control = ctrl;
+ hdr.msg_controllen = sizeof(ctrl);
+
+ cmsg = CMSG_FIRSTHDR(&hdr);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ fd = STDIN_FILENO;
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
+
+ error = sendmsg(sock[1], &hdr, 0);
+ ATF_REQUIRE(error != -1);
+}
+
+/*
+ * Constructs a tmpfile that we can use for testing read(2) and friends.
+ */
+static int __unused
+new_tmpfile(void)
+{
+ char buf[1024];
+ ssize_t rv;
+ size_t written;
+ int fd;
+
+ fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
+ ATF_REQUIRE(fd >= 0);
+
+ written = 0;
+ while (written < TMPFILE_SIZE) {
+ rv = write(fd, buf, sizeof(buf));
+ ATF_REQUIRE(rv > 0);
+
+ written += rv;
+ }
+
+ ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
+ return (fd);
+}
+
+static void
+disable_coredumps(void)
+{
+ struct rlimit rl = { 0 };
+
+ if (setrlimit(RLIMIT_CORE, &rl) == -1)
+ _exit(EX_OSERR);
+}
+
+/*
+ * Replaces stdin with a file that we can actually read from, for tests where
+ * we want a FILE * or fd that we can get data from.
+ */
+static void __unused
+replace_stdin(void)
+{
+ int fd;
+
+ fd = new_tmpfile();
+
+ (void)dup2(fd, STDIN_FILENO);
+ if (fd != STDIN_FILENO)
+ close(fd);
+}
+
+ATF_TC(FD_SET_before_end);
+ATF_TC_HEAD(FD_SET_before_end, tc)
+{
+}
+ATF_TC_BODY(FD_SET_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ fd_set __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = FD_SETSIZE - 1;
+ const size_t __idx __unused = __len - 1;
+
+ FD_SET(__idx, &__stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(FD_SET_end);
+ATF_TC_HEAD(FD_SET_end, tc)
+{
+}
+ATF_TC_BODY(FD_SET_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ fd_set __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = FD_SETSIZE;
+ const size_t __idx __unused = __len - 1;
+
+ FD_SET(__idx, &__stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(FD_SET_after_end);
+ATF_TC_HEAD(FD_SET_after_end, tc)
+{
+}
+ATF_TC_BODY(FD_SET_after_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ fd_set __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = FD_SETSIZE + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ FD_SET(__idx, &__stack.__buf);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(FD_SET_heap_before_end);
+ATF_TC_HEAD(FD_SET_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(FD_SET_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ fd_set * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = FD_SETSIZE - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ FD_SET(__idx, __stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(FD_SET_heap_end);
+ATF_TC_HEAD(FD_SET_heap_end, tc)
+{
+}
+ATF_TC_BODY(FD_SET_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ fd_set * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = FD_SETSIZE;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ FD_SET(__idx, __stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(FD_SET_heap_after_end);
+ATF_TC_HEAD(FD_SET_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(FD_SET_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ fd_set * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = FD_SETSIZE + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ FD_SET(__idx, __stack.__buf);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(FD_CLR_before_end);
+ATF_TC_HEAD(FD_CLR_before_end, tc)
+{
+}
+ATF_TC_BODY(FD_CLR_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ fd_set __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = FD_SETSIZE - 1;
+ const size_t __idx __unused = __len - 1;
+
+ FD_CLR(__idx, &__stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(FD_CLR_end);
+ATF_TC_HEAD(FD_CLR_end, tc)
+{
+}
+ATF_TC_BODY(FD_CLR_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ fd_set __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = FD_SETSIZE;
+ const size_t __idx __unused = __len - 1;
+
+ FD_CLR(__idx, &__stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(FD_CLR_after_end);
+ATF_TC_HEAD(FD_CLR_after_end, tc)
+{
+}
+ATF_TC_BODY(FD_CLR_after_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ fd_set __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = FD_SETSIZE + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ FD_CLR(__idx, &__stack.__buf);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(FD_CLR_heap_before_end);
+ATF_TC_HEAD(FD_CLR_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(FD_CLR_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ fd_set * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = FD_SETSIZE - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ FD_CLR(__idx, __stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(FD_CLR_heap_end);
+ATF_TC_HEAD(FD_CLR_heap_end, tc)
+{
+}
+ATF_TC_BODY(FD_CLR_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ fd_set * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = FD_SETSIZE;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ FD_CLR(__idx, __stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(FD_CLR_heap_after_end);
+ATF_TC_HEAD(FD_CLR_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(FD_CLR_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ fd_set * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = FD_SETSIZE + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ FD_CLR(__idx, __stack.__buf);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(FD_ISSET_before_end);
+ATF_TC_HEAD(FD_ISSET_before_end, tc)
+{
+}
+ATF_TC_BODY(FD_ISSET_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ fd_set __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = FD_SETSIZE - 1;
+ const size_t __idx __unused = __len - 1;
+
+ FD_ISSET(__idx, &__stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(FD_ISSET_end);
+ATF_TC_HEAD(FD_ISSET_end, tc)
+{
+}
+ATF_TC_BODY(FD_ISSET_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ fd_set __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = FD_SETSIZE;
+ const size_t __idx __unused = __len - 1;
+
+ FD_ISSET(__idx, &__stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(FD_ISSET_after_end);
+ATF_TC_HEAD(FD_ISSET_after_end, tc)
+{
+}
+ATF_TC_BODY(FD_ISSET_after_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ fd_set __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = FD_SETSIZE + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ FD_ISSET(__idx, &__stack.__buf);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(FD_ISSET_heap_before_end);
+ATF_TC_HEAD(FD_ISSET_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(FD_ISSET_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ fd_set * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = FD_SETSIZE - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ FD_ISSET(__idx, __stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(FD_ISSET_heap_end);
+ATF_TC_HEAD(FD_ISSET_heap_end, tc)
+{
+}
+ATF_TC_BODY(FD_ISSET_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ fd_set * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = FD_SETSIZE;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ FD_ISSET(__idx, __stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(FD_ISSET_heap_after_end);
+ATF_TC_HEAD(FD_ISSET_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(FD_ISSET_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ fd_set * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = FD_SETSIZE + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ FD_ISSET(__idx, __stack.__buf);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, FD_SET_before_end);
+ ATF_TP_ADD_TC(tp, FD_SET_end);
+ ATF_TP_ADD_TC(tp, FD_SET_after_end);
+ ATF_TP_ADD_TC(tp, FD_SET_heap_before_end);
+ ATF_TP_ADD_TC(tp, FD_SET_heap_end);
+ ATF_TP_ADD_TC(tp, FD_SET_heap_after_end);
+ ATF_TP_ADD_TC(tp, FD_CLR_before_end);
+ ATF_TP_ADD_TC(tp, FD_CLR_end);
+ ATF_TP_ADD_TC(tp, FD_CLR_after_end);
+ ATF_TP_ADD_TC(tp, FD_CLR_heap_before_end);
+ ATF_TP_ADD_TC(tp, FD_CLR_heap_end);
+ ATF_TP_ADD_TC(tp, FD_CLR_heap_after_end);
+ ATF_TP_ADD_TC(tp, FD_ISSET_before_end);
+ ATF_TP_ADD_TC(tp, FD_ISSET_end);
+ ATF_TP_ADD_TC(tp, FD_ISSET_after_end);
+ ATF_TP_ADD_TC(tp, FD_ISSET_heap_before_end);
+ ATF_TP_ADD_TC(tp, FD_ISSET_heap_end);
+ ATF_TP_ADD_TC(tp, FD_ISSET_heap_after_end);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/secure/fortify_signal_test.c b/lib/libc/tests/secure/fortify_signal_test.c
new file mode 100644
index 000000000000..03cfb9a9a13a
--- /dev/null
+++ b/lib/libc/tests/secure/fortify_signal_test.c
@@ -0,0 +1,316 @@
+/* @generated by `generate-fortify-tests.lua "signal"` */
+
+#define _FORTIFY_SOURCE 2
+#define TMPFILE_SIZE (1024 * 32)
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/random.h>
+#include <sys/resource.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <atf-c.h>
+
+static FILE * __unused
+new_fp(size_t __len)
+{
+ static char fpbuf[LINE_MAX];
+ FILE *fp;
+
+ ATF_REQUIRE(__len <= sizeof(fpbuf));
+
+ memset(fpbuf, 'A', sizeof(fpbuf) - 1);
+ fpbuf[sizeof(fpbuf) - 1] = '\0';
+
+ fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
+ ATF_REQUIRE(fp != NULL);
+
+ return (fp);
+}
+
+/*
+ * Create a new symlink to use for readlink(2) style tests, we'll just use a
+ * random target name to have something interesting to look at.
+ */
+static const char * __unused
+new_symlink(size_t __len)
+{
+ static const char linkname[] = "link";
+ char target[MAXNAMLEN];
+ int error;
+
+ ATF_REQUIRE(__len <= sizeof(target));
+
+ arc4random_buf(target, sizeof(target));
+
+ error = unlink(linkname);
+ ATF_REQUIRE(error == 0 || errno == ENOENT);
+
+ error = symlink(target, linkname);
+ ATF_REQUIRE(error == 0);
+
+ return (linkname);
+}
+
+/*
+ * For our purposes, first descriptor will be the reader; we'll send both
+ * raw data and a control message over it so that the result can be used for
+ * any of our recv*() tests.
+ */
+static void __unused
+new_socket(int sock[2])
+{
+ unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 };
+ static char sockbuf[256];
+ ssize_t rv;
+ size_t total = 0;
+ struct msghdr hdr = { 0 };
+ struct cmsghdr *cmsg;
+ int error, fd;
+
+ error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
+ ATF_REQUIRE(error == 0);
+
+ while (total != sizeof(sockbuf)) {
+ rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0);
+
+ ATF_REQUIRE_MSG(rv > 0,
+ "expected bytes sent, got %zd with %zu left (size %zu, total %zu)",
+ rv, sizeof(sockbuf) - total, sizeof(sockbuf), total);
+ ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf),
+ "%zd exceeds total %zu", rv, sizeof(sockbuf));
+ total += rv;
+ }
+
+ hdr.msg_control = ctrl;
+ hdr.msg_controllen = sizeof(ctrl);
+
+ cmsg = CMSG_FIRSTHDR(&hdr);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ fd = STDIN_FILENO;
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
+
+ error = sendmsg(sock[1], &hdr, 0);
+ ATF_REQUIRE(error != -1);
+}
+
+/*
+ * Constructs a tmpfile that we can use for testing read(2) and friends.
+ */
+static int __unused
+new_tmpfile(void)
+{
+ char buf[1024];
+ ssize_t rv;
+ size_t written;
+ int fd;
+
+ fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
+ ATF_REQUIRE(fd >= 0);
+
+ written = 0;
+ while (written < TMPFILE_SIZE) {
+ rv = write(fd, buf, sizeof(buf));
+ ATF_REQUIRE(rv > 0);
+
+ written += rv;
+ }
+
+ ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
+ return (fd);
+}
+
+static void
+disable_coredumps(void)
+{
+ struct rlimit rl = { 0 };
+
+ if (setrlimit(RLIMIT_CORE, &rl) == -1)
+ _exit(EX_OSERR);
+}
+
+/*
+ * Replaces stdin with a file that we can actually read from, for tests where
+ * we want a FILE * or fd that we can get data from.
+ */
+static void __unused
+replace_stdin(void)
+{
+ int fd;
+
+ fd = new_tmpfile();
+
+ (void)dup2(fd, STDIN_FILENO);
+ if (fd != STDIN_FILENO)
+ close(fd);
+}
+
+ATF_TC(sig2str_before_end);
+ATF_TC_HEAD(sig2str_before_end, tc)
+{
+}
+ATF_TC_BODY(sig2str_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[SIG2STR_MAX + 1];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = SIG2STR_MAX + 1;
+ const size_t __idx __unused = __len - 1;
+
+ sig2str(1, __stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(sig2str_end);
+ATF_TC_HEAD(sig2str_end, tc)
+{
+}
+ATF_TC_BODY(sig2str_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[SIG2STR_MAX];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = SIG2STR_MAX;
+ const size_t __idx __unused = __len - 1;
+
+ sig2str(1, __stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(sig2str_heap_before_end);
+ATF_TC_HEAD(sig2str_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(sig2str_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (SIG2STR_MAX + 1);
+ const size_t __len = SIG2STR_MAX + 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ sig2str(1, __stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(sig2str_heap_end);
+ATF_TC_HEAD(sig2str_heap_end, tc)
+{
+}
+ATF_TC_BODY(sig2str_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (SIG2STR_MAX);
+ const size_t __len = SIG2STR_MAX;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ sig2str(1, __stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(sig2str_heap_after_end);
+ATF_TC_HEAD(sig2str_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(sig2str_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (SIG2STR_MAX - 1);
+ const size_t __len = SIG2STR_MAX - 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ sig2str(1, __stack.__buf);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, sig2str_before_end);
+ ATF_TP_ADD_TC(tp, sig2str_end);
+ ATF_TP_ADD_TC(tp, sig2str_heap_before_end);
+ ATF_TP_ADD_TC(tp, sig2str_heap_end);
+ ATF_TP_ADD_TC(tp, sig2str_heap_after_end);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/secure/fortify_socket_test.c b/lib/libc/tests/secure/fortify_socket_test.c
new file mode 100644
index 000000000000..3d2dc86f4e1c
--- /dev/null
+++ b/lib/libc/tests/secure/fortify_socket_test.c
@@ -0,0 +1,1971 @@
+/* @generated by `generate-fortify-tests.lua "socket"` */
+
+#define _FORTIFY_SOURCE 2
+#define TMPFILE_SIZE (1024 * 32)
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/random.h>
+#include <sys/resource.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <atf-c.h>
+
+static FILE * __unused
+new_fp(size_t __len)
+{
+ static char fpbuf[LINE_MAX];
+ FILE *fp;
+
+ ATF_REQUIRE(__len <= sizeof(fpbuf));
+
+ memset(fpbuf, 'A', sizeof(fpbuf) - 1);
+ fpbuf[sizeof(fpbuf) - 1] = '\0';
+
+ fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
+ ATF_REQUIRE(fp != NULL);
+
+ return (fp);
+}
+
+/*
+ * Create a new symlink to use for readlink(2) style tests, we'll just use a
+ * random target name to have something interesting to look at.
+ */
+static const char * __unused
+new_symlink(size_t __len)
+{
+ static const char linkname[] = "link";
+ char target[MAXNAMLEN];
+ int error;
+
+ ATF_REQUIRE(__len <= sizeof(target));
+
+ arc4random_buf(target, sizeof(target));
+
+ error = unlink(linkname);
+ ATF_REQUIRE(error == 0 || errno == ENOENT);
+
+ error = symlink(target, linkname);
+ ATF_REQUIRE(error == 0);
+
+ return (linkname);
+}
+
+/*
+ * For our purposes, first descriptor will be the reader; we'll send both
+ * raw data and a control message over it so that the result can be used for
+ * any of our recv*() tests.
+ */
+static void __unused
+new_socket(int sock[2])
+{
+ unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 };
+ static char sockbuf[256];
+ ssize_t rv;
+ size_t total = 0;
+ struct msghdr hdr = { 0 };
+ struct cmsghdr *cmsg;
+ int error, fd;
+
+ error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
+ ATF_REQUIRE(error == 0);
+
+ while (total != sizeof(sockbuf)) {
+ rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0);
+
+ ATF_REQUIRE_MSG(rv > 0,
+ "expected bytes sent, got %zd with %zu left (size %zu, total %zu)",
+ rv, sizeof(sockbuf) - total, sizeof(sockbuf), total);
+ ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf),
+ "%zd exceeds total %zu", rv, sizeof(sockbuf));
+ total += rv;
+ }
+
+ hdr.msg_control = ctrl;
+ hdr.msg_controllen = sizeof(ctrl);
+
+ cmsg = CMSG_FIRSTHDR(&hdr);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ fd = STDIN_FILENO;
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
+
+ error = sendmsg(sock[1], &hdr, 0);
+ ATF_REQUIRE(error != -1);
+}
+
+/*
+ * Constructs a tmpfile that we can use for testing read(2) and friends.
+ */
+static int __unused
+new_tmpfile(void)
+{
+ char buf[1024];
+ ssize_t rv;
+ size_t written;
+ int fd;
+
+ fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
+ ATF_REQUIRE(fd >= 0);
+
+ written = 0;
+ while (written < TMPFILE_SIZE) {
+ rv = write(fd, buf, sizeof(buf));
+ ATF_REQUIRE(rv > 0);
+
+ written += rv;
+ }
+
+ ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
+ return (fd);
+}
+
+static void
+disable_coredumps(void)
+{
+ struct rlimit rl = { 0 };
+
+ if (setrlimit(RLIMIT_CORE, &rl) == -1)
+ _exit(EX_OSERR);
+}
+
+/*
+ * Replaces stdin with a file that we can actually read from, for tests where
+ * we want a FILE * or fd that we can get data from.
+ */
+static void __unused
+replace_stdin(void)
+{
+ int fd;
+
+ fd = new_tmpfile();
+
+ (void)dup2(fd, STDIN_FILENO);
+ if (fd != STDIN_FILENO)
+ close(fd);
+}
+
+ATF_TC(getpeername_before_end);
+ATF_TC_HEAD(getpeername_before_end, tc)
+{
+}
+ATF_TC_BODY(getpeername_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = sizeof(struct sockaddr) - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ socklen_t socklen;
+ new_socket(sock);
+ socklen = __len;
+
+ getpeername(sock[0], &__stack.__buf, &socklen);
+#undef BUF
+
+}
+
+ATF_TC(getpeername_end);
+ATF_TC_HEAD(getpeername_end, tc)
+{
+}
+ATF_TC_BODY(getpeername_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = sizeof(struct sockaddr);
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ socklen_t socklen;
+ new_socket(sock);
+ socklen = __len;
+
+ getpeername(sock[0], &__stack.__buf, &socklen);
+#undef BUF
+
+}
+
+ATF_TC(getpeername_heap_before_end);
+ATF_TC_HEAD(getpeername_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(getpeername_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = sizeof(struct sockaddr) - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ socklen_t socklen;
+ __stack.__buf = malloc(__bufsz);
+ new_socket(sock);
+ socklen = __len;
+
+ getpeername(sock[0], __stack.__buf, &socklen);
+#undef BUF
+
+}
+
+ATF_TC(getpeername_heap_end);
+ATF_TC_HEAD(getpeername_heap_end, tc)
+{
+}
+ATF_TC_BODY(getpeername_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = sizeof(struct sockaddr);
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ socklen_t socklen;
+ __stack.__buf = malloc(__bufsz);
+ new_socket(sock);
+ socklen = __len;
+
+ getpeername(sock[0], __stack.__buf, &socklen);
+#undef BUF
+
+}
+
+ATF_TC(getpeername_heap_after_end);
+ATF_TC_HEAD(getpeername_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(getpeername_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = sizeof(struct sockaddr) + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ int sock[2] = { -1, -1 };
+ socklen_t socklen;
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ new_socket(sock);
+ socklen = __len;
+
+ getpeername(sock[0], __stack.__buf, &socklen);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(getsockname_before_end);
+ATF_TC_HEAD(getsockname_before_end, tc)
+{
+}
+ATF_TC_BODY(getsockname_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = sizeof(struct sockaddr) - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ socklen_t socklen;
+ new_socket(sock);
+ socklen = __len;
+
+ getsockname(sock[0], &__stack.__buf, &socklen);
+#undef BUF
+
+}
+
+ATF_TC(getsockname_end);
+ATF_TC_HEAD(getsockname_end, tc)
+{
+}
+ATF_TC_BODY(getsockname_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = sizeof(struct sockaddr);
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ socklen_t socklen;
+ new_socket(sock);
+ socklen = __len;
+
+ getsockname(sock[0], &__stack.__buf, &socklen);
+#undef BUF
+
+}
+
+ATF_TC(getsockname_heap_before_end);
+ATF_TC_HEAD(getsockname_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(getsockname_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = sizeof(struct sockaddr) - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ socklen_t socklen;
+ __stack.__buf = malloc(__bufsz);
+ new_socket(sock);
+ socklen = __len;
+
+ getsockname(sock[0], __stack.__buf, &socklen);
+#undef BUF
+
+}
+
+ATF_TC(getsockname_heap_end);
+ATF_TC_HEAD(getsockname_heap_end, tc)
+{
+}
+ATF_TC_BODY(getsockname_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = sizeof(struct sockaddr);
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ socklen_t socklen;
+ __stack.__buf = malloc(__bufsz);
+ new_socket(sock);
+ socklen = __len;
+
+ getsockname(sock[0], __stack.__buf, &socklen);
+#undef BUF
+
+}
+
+ATF_TC(getsockname_heap_after_end);
+ATF_TC_HEAD(getsockname_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(getsockname_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = sizeof(struct sockaddr) + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ int sock[2] = { -1, -1 };
+ socklen_t socklen;
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ new_socket(sock);
+ socklen = __len;
+
+ getsockname(sock[0], __stack.__buf, &socklen);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(recv_before_end);
+ATF_TC_HEAD(recv_before_end, tc)
+{
+}
+ATF_TC_BODY(recv_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+
+ new_socket(sock);
+
+ recv(sock[0], __stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(recv_end);
+ATF_TC_HEAD(recv_end, tc)
+{
+}
+ATF_TC_BODY(recv_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+
+ new_socket(sock);
+
+ recv(sock[0], __stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(recv_heap_before_end);
+ATF_TC_HEAD(recv_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(recv_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+
+ __stack.__buf = malloc(__bufsz);
+ new_socket(sock);
+
+ recv(sock[0], __stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(recv_heap_end);
+ATF_TC_HEAD(recv_heap_end, tc)
+{
+}
+ATF_TC_BODY(recv_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+
+ __stack.__buf = malloc(__bufsz);
+ new_socket(sock);
+
+ recv(sock[0], __stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(recv_heap_after_end);
+ATF_TC_HEAD(recv_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(recv_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ int sock[2] = { -1, -1 };
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ new_socket(sock);
+
+ recv(sock[0], __stack.__buf, __len, 0);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(recvfrom_before_end);
+ATF_TC_HEAD(recvfrom_before_end, tc)
+{
+}
+ATF_TC_BODY(recvfrom_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+
+ new_socket(sock);
+
+ recvfrom(sock[0], __stack.__buf, __len, 0, NULL, NULL);
+#undef BUF
+
+}
+
+ATF_TC(recvfrom_end);
+ATF_TC_HEAD(recvfrom_end, tc)
+{
+}
+ATF_TC_BODY(recvfrom_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+
+ new_socket(sock);
+
+ recvfrom(sock[0], __stack.__buf, __len, 0, NULL, NULL);
+#undef BUF
+
+}
+
+ATF_TC(recvfrom_heap_before_end);
+ATF_TC_HEAD(recvfrom_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(recvfrom_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+
+ __stack.__buf = malloc(__bufsz);
+ new_socket(sock);
+
+ recvfrom(sock[0], __stack.__buf, __len, 0, NULL, NULL);
+#undef BUF
+
+}
+
+ATF_TC(recvfrom_heap_end);
+ATF_TC_HEAD(recvfrom_heap_end, tc)
+{
+}
+ATF_TC_BODY(recvfrom_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+
+ __stack.__buf = malloc(__bufsz);
+ new_socket(sock);
+
+ recvfrom(sock[0], __stack.__buf, __len, 0, NULL, NULL);
+#undef BUF
+
+}
+
+ATF_TC(recvfrom_heap_after_end);
+ATF_TC_HEAD(recvfrom_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(recvfrom_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ int sock[2] = { -1, -1 };
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ new_socket(sock);
+
+ recvfrom(sock[0], __stack.__buf, __len, 0, NULL, NULL);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(recvfrom_sockaddr_before_end);
+ATF_TC_HEAD(recvfrom_sockaddr_before_end, tc)
+{
+}
+ATF_TC_BODY(recvfrom_sockaddr_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = sizeof(struct sockaddr) - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ char data[16];
+ socklen_t socklen;
+
+ new_socket(sock);
+ socklen = __len;
+
+ recvfrom(sock[0], data, sizeof(data), 0, &__stack.__buf, &socklen);
+#undef BUF
+
+}
+
+ATF_TC(recvfrom_sockaddr_end);
+ATF_TC_HEAD(recvfrom_sockaddr_end, tc)
+{
+}
+ATF_TC_BODY(recvfrom_sockaddr_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = sizeof(struct sockaddr);
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ char data[16];
+ socklen_t socklen;
+
+ new_socket(sock);
+ socklen = __len;
+
+ recvfrom(sock[0], data, sizeof(data), 0, &__stack.__buf, &socklen);
+#undef BUF
+
+}
+
+ATF_TC(recvfrom_sockaddr_heap_before_end);
+ATF_TC_HEAD(recvfrom_sockaddr_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(recvfrom_sockaddr_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = sizeof(struct sockaddr) - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ char data[16];
+ socklen_t socklen;
+
+ __stack.__buf = malloc(__bufsz);
+ new_socket(sock);
+ socklen = __len;
+
+ recvfrom(sock[0], data, sizeof(data), 0, __stack.__buf, &socklen);
+#undef BUF
+
+}
+
+ATF_TC(recvfrom_sockaddr_heap_end);
+ATF_TC_HEAD(recvfrom_sockaddr_heap_end, tc)
+{
+}
+ATF_TC_BODY(recvfrom_sockaddr_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = sizeof(struct sockaddr);
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ char data[16];
+ socklen_t socklen;
+
+ __stack.__buf = malloc(__bufsz);
+ new_socket(sock);
+ socklen = __len;
+
+ recvfrom(sock[0], data, sizeof(data), 0, __stack.__buf, &socklen);
+#undef BUF
+
+}
+
+ATF_TC(recvfrom_sockaddr_heap_after_end);
+ATF_TC_HEAD(recvfrom_sockaddr_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(recvfrom_sockaddr_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = sizeof(struct sockaddr) + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ int sock[2] = { -1, -1 };
+ char data[16];
+ socklen_t socklen;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ new_socket(sock);
+ socklen = __len;
+
+ recvfrom(sock[0], data, sizeof(data), 0, __stack.__buf, &socklen);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(recvmsg_msg_name_before_end);
+ATF_TC_HEAD(recvmsg_msg_name_before_end, tc)
+{
+}
+ATF_TC_BODY(recvmsg_msg_name_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = sizeof(struct sockaddr) - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ struct msghdr msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = BUF;
+ msg.msg_namelen = __len;
+
+ recvmsg(sock[0], &msg, 0);
+#undef BUF
+
+}
+
+ATF_TC(recvmsg_msg_name_end);
+ATF_TC_HEAD(recvmsg_msg_name_end, tc)
+{
+}
+ATF_TC_BODY(recvmsg_msg_name_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = sizeof(struct sockaddr);
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ struct msghdr msg;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = BUF;
+ msg.msg_namelen = __len;
+
+ recvmsg(sock[0], &msg, 0);
+#undef BUF
+
+}
+
+ATF_TC(recvmsg_msg_name_heap_before_end);
+ATF_TC_HEAD(recvmsg_msg_name_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(recvmsg_msg_name_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = sizeof(struct sockaddr) - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ struct msghdr msg;
+
+ __stack.__buf = malloc(__bufsz);
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = BUF;
+ msg.msg_namelen = __len;
+
+ recvmsg(sock[0], &msg, 0);
+#undef BUF
+
+}
+
+ATF_TC(recvmsg_msg_name_heap_end);
+ATF_TC_HEAD(recvmsg_msg_name_heap_end, tc)
+{
+}
+ATF_TC_BODY(recvmsg_msg_name_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = sizeof(struct sockaddr);
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ struct msghdr msg;
+
+ __stack.__buf = malloc(__bufsz);
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = BUF;
+ msg.msg_namelen = __len;
+
+ recvmsg(sock[0], &msg, 0);
+#undef BUF
+
+}
+
+ATF_TC(recvmsg_msg_name_heap_after_end);
+ATF_TC_HEAD(recvmsg_msg_name_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(recvmsg_msg_name_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct sockaddr * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (1);
+ const size_t __len = sizeof(struct sockaddr) + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ int sock[2] = { -1, -1 };
+ struct msghdr msg;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = BUF;
+ msg.msg_namelen = __len;
+
+ recvmsg(sock[0], &msg, 0);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(recvmsg_msg_iov_before_end);
+ATF_TC_HEAD(recvmsg_msg_iov_before_end, tc)
+{
+}
+ATF_TC_BODY(recvmsg_msg_iov_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ struct msghdr msg;
+ struct iovec iov[2];
+
+ memset(&msg, 0, sizeof(msg));
+ memset(&iov[0], 0, sizeof(iov));
+
+ /*
+ * We position the buffer second just so that we can confirm that the
+ * fortification bits are traversing the iovec correctly.
+ */
+ iov[1].iov_base = BUF;
+ iov[1].iov_len = __len;
+
+ msg.msg_iov = &iov[0];
+ msg.msg_iovlen = nitems(iov);
+
+ recvmsg(sock[0], &msg, 0);
+#undef BUF
+
+}
+
+ATF_TC(recvmsg_msg_iov_end);
+ATF_TC_HEAD(recvmsg_msg_iov_end, tc)
+{
+}
+ATF_TC_BODY(recvmsg_msg_iov_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ struct msghdr msg;
+ struct iovec iov[2];
+
+ memset(&msg, 0, sizeof(msg));
+ memset(&iov[0], 0, sizeof(iov));
+
+ /*
+ * We position the buffer second just so that we can confirm that the
+ * fortification bits are traversing the iovec correctly.
+ */
+ iov[1].iov_base = BUF;
+ iov[1].iov_len = __len;
+
+ msg.msg_iov = &iov[0];
+ msg.msg_iovlen = nitems(iov);
+
+ recvmsg(sock[0], &msg, 0);
+#undef BUF
+
+}
+
+ATF_TC(recvmsg_msg_iov_heap_before_end);
+ATF_TC_HEAD(recvmsg_msg_iov_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(recvmsg_msg_iov_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ struct msghdr msg;
+ struct iovec iov[2];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(&msg, 0, sizeof(msg));
+ memset(&iov[0], 0, sizeof(iov));
+
+ /*
+ * We position the buffer second just so that we can confirm that the
+ * fortification bits are traversing the iovec correctly.
+ */
+ iov[1].iov_base = BUF;
+ iov[1].iov_len = __len;
+
+ msg.msg_iov = &iov[0];
+ msg.msg_iovlen = nitems(iov);
+
+ recvmsg(sock[0], &msg, 0);
+#undef BUF
+
+}
+
+ATF_TC(recvmsg_msg_iov_heap_end);
+ATF_TC_HEAD(recvmsg_msg_iov_heap_end, tc)
+{
+}
+ATF_TC_BODY(recvmsg_msg_iov_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ struct msghdr msg;
+ struct iovec iov[2];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(&msg, 0, sizeof(msg));
+ memset(&iov[0], 0, sizeof(iov));
+
+ /*
+ * We position the buffer second just so that we can confirm that the
+ * fortification bits are traversing the iovec correctly.
+ */
+ iov[1].iov_base = BUF;
+ iov[1].iov_len = __len;
+
+ msg.msg_iov = &iov[0];
+ msg.msg_iovlen = nitems(iov);
+
+ recvmsg(sock[0], &msg, 0);
+#undef BUF
+
+}
+
+ATF_TC(recvmsg_msg_iov_heap_after_end);
+ATF_TC_HEAD(recvmsg_msg_iov_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(recvmsg_msg_iov_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ int sock[2] = { -1, -1 };
+ struct msghdr msg;
+ struct iovec iov[2];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(&msg, 0, sizeof(msg));
+ memset(&iov[0], 0, sizeof(iov));
+
+ /*
+ * We position the buffer second just so that we can confirm that the
+ * fortification bits are traversing the iovec correctly.
+ */
+ iov[1].iov_base = BUF;
+ iov[1].iov_len = __len;
+
+ msg.msg_iov = &iov[0];
+ msg.msg_iovlen = nitems(iov);
+
+ recvmsg(sock[0], &msg, 0);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(recvmsg_msg_control_before_end);
+ATF_TC_HEAD(recvmsg_msg_control_before_end, tc)
+{
+}
+ATF_TC_BODY(recvmsg_msg_control_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[CMSG_SPACE(sizeof(int))];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = CMSG_SPACE(sizeof(int)) - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ struct msghdr msg;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_control = BUF;
+ msg.msg_controllen = __len;
+
+ recvmsg(sock[0], &msg, 0);
+#undef BUF
+
+}
+
+ATF_TC(recvmsg_msg_control_end);
+ATF_TC_HEAD(recvmsg_msg_control_end, tc)
+{
+}
+ATF_TC_BODY(recvmsg_msg_control_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[CMSG_SPACE(sizeof(int))];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = CMSG_SPACE(sizeof(int));
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ struct msghdr msg;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_control = BUF;
+ msg.msg_controllen = __len;
+
+ recvmsg(sock[0], &msg, 0);
+#undef BUF
+
+}
+
+ATF_TC(recvmsg_msg_control_heap_before_end);
+ATF_TC_HEAD(recvmsg_msg_control_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(recvmsg_msg_control_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (CMSG_SPACE(sizeof(int)));
+ const size_t __len = CMSG_SPACE(sizeof(int)) - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ struct msghdr msg;
+
+ __stack.__buf = malloc(__bufsz);
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_control = BUF;
+ msg.msg_controllen = __len;
+
+ recvmsg(sock[0], &msg, 0);
+#undef BUF
+
+}
+
+ATF_TC(recvmsg_msg_control_heap_end);
+ATF_TC_HEAD(recvmsg_msg_control_heap_end, tc)
+{
+}
+ATF_TC_BODY(recvmsg_msg_control_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (CMSG_SPACE(sizeof(int)));
+ const size_t __len = CMSG_SPACE(sizeof(int));
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ struct msghdr msg;
+
+ __stack.__buf = malloc(__bufsz);
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_control = BUF;
+ msg.msg_controllen = __len;
+
+ recvmsg(sock[0], &msg, 0);
+#undef BUF
+
+}
+
+ATF_TC(recvmsg_msg_control_heap_after_end);
+ATF_TC_HEAD(recvmsg_msg_control_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(recvmsg_msg_control_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (CMSG_SPACE(sizeof(int)));
+ const size_t __len = CMSG_SPACE(sizeof(int)) + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ int sock[2] = { -1, -1 };
+ struct msghdr msg;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_control = BUF;
+ msg.msg_controllen = __len;
+
+ recvmsg(sock[0], &msg, 0);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(recvmmsg_msgvec_before_end);
+ATF_TC_HEAD(recvmmsg_msgvec_before_end, tc)
+{
+}
+ATF_TC_BODY(recvmmsg_msgvec_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct mmsghdr __buf[2];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 2 - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+
+ recvmmsg(sock[0], __stack.__buf, __len, 0, NULL);
+#undef BUF
+
+}
+
+ATF_TC(recvmmsg_msgvec_end);
+ATF_TC_HEAD(recvmmsg_msgvec_end, tc)
+{
+}
+ATF_TC_BODY(recvmmsg_msgvec_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct mmsghdr __buf[2];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 2;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+
+ recvmmsg(sock[0], __stack.__buf, __len, 0, NULL);
+#undef BUF
+
+}
+
+ATF_TC(recvmmsg_msgvec_after_end);
+ATF_TC_HEAD(recvmmsg_msgvec_after_end, tc)
+{
+}
+ATF_TC_BODY(recvmmsg_msgvec_after_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct mmsghdr __buf[2];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 2 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ int sock[2] = { -1, -1 };
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ recvmmsg(sock[0], __stack.__buf, __len, 0, NULL);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(recvmmsg_msgvec_heap_before_end);
+ATF_TC_HEAD(recvmmsg_msgvec_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(recvmmsg_msgvec_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct mmsghdr * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (2);
+ const size_t __len = 2 - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+
+ __stack.__buf = malloc(__bufsz);
+
+ recvmmsg(sock[0], __stack.__buf, __len, 0, NULL);
+#undef BUF
+
+}
+
+ATF_TC(recvmmsg_msgvec_heap_end);
+ATF_TC_HEAD(recvmmsg_msgvec_heap_end, tc)
+{
+}
+ATF_TC_BODY(recvmmsg_msgvec_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct mmsghdr * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (2);
+ const size_t __len = 2;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+
+ __stack.__buf = malloc(__bufsz);
+
+ recvmmsg(sock[0], __stack.__buf, __len, 0, NULL);
+#undef BUF
+
+}
+
+ATF_TC(recvmmsg_msgvec_heap_after_end);
+ATF_TC_HEAD(recvmmsg_msgvec_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(recvmmsg_msgvec_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct mmsghdr * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (2);
+ const size_t __len = 2 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ int sock[2] = { -1, -1 };
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ recvmmsg(sock[0], __stack.__buf, __len, 0, NULL);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(recvmmsg_msghdr_before_end);
+ATF_TC_HEAD(recvmmsg_msghdr_before_end, tc)
+{
+}
+ATF_TC_BODY(recvmmsg_msghdr_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ struct mmsghdr msgvec[2];
+
+ memset(&msgvec[0], 0, sizeof(msgvec));
+
+ /*
+ * Same as above, make sure fortification isn't ignoring n > 1 elements
+ * of the msgvec.
+ */
+ msgvec[1].msg_hdr.msg_control = BUF;
+ msgvec[1].msg_hdr.msg_controllen = __len;
+
+ recvmmsg(sock[0], &msgvec[0], nitems(msgvec), 0, NULL);
+#undef BUF
+
+}
+
+ATF_TC(recvmmsg_msghdr_end);
+ATF_TC_HEAD(recvmmsg_msghdr_end, tc)
+{
+}
+ATF_TC_BODY(recvmmsg_msghdr_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ struct mmsghdr msgvec[2];
+
+ memset(&msgvec[0], 0, sizeof(msgvec));
+
+ /*
+ * Same as above, make sure fortification isn't ignoring n > 1 elements
+ * of the msgvec.
+ */
+ msgvec[1].msg_hdr.msg_control = BUF;
+ msgvec[1].msg_hdr.msg_controllen = __len;
+
+ recvmmsg(sock[0], &msgvec[0], nitems(msgvec), 0, NULL);
+#undef BUF
+
+}
+
+ATF_TC(recvmmsg_msghdr_heap_before_end);
+ATF_TC_HEAD(recvmmsg_msghdr_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(recvmmsg_msghdr_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ struct mmsghdr msgvec[2];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(&msgvec[0], 0, sizeof(msgvec));
+
+ /*
+ * Same as above, make sure fortification isn't ignoring n > 1 elements
+ * of the msgvec.
+ */
+ msgvec[1].msg_hdr.msg_control = BUF;
+ msgvec[1].msg_hdr.msg_controllen = __len;
+
+ recvmmsg(sock[0], &msgvec[0], nitems(msgvec), 0, NULL);
+#undef BUF
+
+}
+
+ATF_TC(recvmmsg_msghdr_heap_end);
+ATF_TC_HEAD(recvmmsg_msghdr_heap_end, tc)
+{
+}
+ATF_TC_BODY(recvmmsg_msghdr_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ int sock[2] = { -1, -1 };
+ struct mmsghdr msgvec[2];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(&msgvec[0], 0, sizeof(msgvec));
+
+ /*
+ * Same as above, make sure fortification isn't ignoring n > 1 elements
+ * of the msgvec.
+ */
+ msgvec[1].msg_hdr.msg_control = BUF;
+ msgvec[1].msg_hdr.msg_controllen = __len;
+
+ recvmmsg(sock[0], &msgvec[0], nitems(msgvec), 0, NULL);
+#undef BUF
+
+}
+
+ATF_TC(recvmmsg_msghdr_heap_after_end);
+ATF_TC_HEAD(recvmmsg_msghdr_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(recvmmsg_msghdr_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ int sock[2] = { -1, -1 };
+ struct mmsghdr msgvec[2];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(&msgvec[0], 0, sizeof(msgvec));
+
+ /*
+ * Same as above, make sure fortification isn't ignoring n > 1 elements
+ * of the msgvec.
+ */
+ msgvec[1].msg_hdr.msg_control = BUF;
+ msgvec[1].msg_hdr.msg_controllen = __len;
+
+ recvmmsg(sock[0], &msgvec[0], nitems(msgvec), 0, NULL);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, getpeername_before_end);
+ ATF_TP_ADD_TC(tp, getpeername_end);
+ ATF_TP_ADD_TC(tp, getpeername_heap_before_end);
+ ATF_TP_ADD_TC(tp, getpeername_heap_end);
+ ATF_TP_ADD_TC(tp, getpeername_heap_after_end);
+ ATF_TP_ADD_TC(tp, getsockname_before_end);
+ ATF_TP_ADD_TC(tp, getsockname_end);
+ ATF_TP_ADD_TC(tp, getsockname_heap_before_end);
+ ATF_TP_ADD_TC(tp, getsockname_heap_end);
+ ATF_TP_ADD_TC(tp, getsockname_heap_after_end);
+ ATF_TP_ADD_TC(tp, recv_before_end);
+ ATF_TP_ADD_TC(tp, recv_end);
+ ATF_TP_ADD_TC(tp, recv_heap_before_end);
+ ATF_TP_ADD_TC(tp, recv_heap_end);
+ ATF_TP_ADD_TC(tp, recv_heap_after_end);
+ ATF_TP_ADD_TC(tp, recvfrom_before_end);
+ ATF_TP_ADD_TC(tp, recvfrom_end);
+ ATF_TP_ADD_TC(tp, recvfrom_heap_before_end);
+ ATF_TP_ADD_TC(tp, recvfrom_heap_end);
+ ATF_TP_ADD_TC(tp, recvfrom_heap_after_end);
+ ATF_TP_ADD_TC(tp, recvfrom_sockaddr_before_end);
+ ATF_TP_ADD_TC(tp, recvfrom_sockaddr_end);
+ ATF_TP_ADD_TC(tp, recvfrom_sockaddr_heap_before_end);
+ ATF_TP_ADD_TC(tp, recvfrom_sockaddr_heap_end);
+ ATF_TP_ADD_TC(tp, recvfrom_sockaddr_heap_after_end);
+ ATF_TP_ADD_TC(tp, recvmsg_msg_name_before_end);
+ ATF_TP_ADD_TC(tp, recvmsg_msg_name_end);
+ ATF_TP_ADD_TC(tp, recvmsg_msg_name_heap_before_end);
+ ATF_TP_ADD_TC(tp, recvmsg_msg_name_heap_end);
+ ATF_TP_ADD_TC(tp, recvmsg_msg_name_heap_after_end);
+ ATF_TP_ADD_TC(tp, recvmsg_msg_iov_before_end);
+ ATF_TP_ADD_TC(tp, recvmsg_msg_iov_end);
+ ATF_TP_ADD_TC(tp, recvmsg_msg_iov_heap_before_end);
+ ATF_TP_ADD_TC(tp, recvmsg_msg_iov_heap_end);
+ ATF_TP_ADD_TC(tp, recvmsg_msg_iov_heap_after_end);
+ ATF_TP_ADD_TC(tp, recvmsg_msg_control_before_end);
+ ATF_TP_ADD_TC(tp, recvmsg_msg_control_end);
+ ATF_TP_ADD_TC(tp, recvmsg_msg_control_heap_before_end);
+ ATF_TP_ADD_TC(tp, recvmsg_msg_control_heap_end);
+ ATF_TP_ADD_TC(tp, recvmsg_msg_control_heap_after_end);
+ ATF_TP_ADD_TC(tp, recvmmsg_msgvec_before_end);
+ ATF_TP_ADD_TC(tp, recvmmsg_msgvec_end);
+ ATF_TP_ADD_TC(tp, recvmmsg_msgvec_after_end);
+ ATF_TP_ADD_TC(tp, recvmmsg_msgvec_heap_before_end);
+ ATF_TP_ADD_TC(tp, recvmmsg_msgvec_heap_end);
+ ATF_TP_ADD_TC(tp, recvmmsg_msgvec_heap_after_end);
+ ATF_TP_ADD_TC(tp, recvmmsg_msghdr_before_end);
+ ATF_TP_ADD_TC(tp, recvmmsg_msghdr_end);
+ ATF_TP_ADD_TC(tp, recvmmsg_msghdr_heap_before_end);
+ ATF_TP_ADD_TC(tp, recvmmsg_msghdr_heap_end);
+ ATF_TP_ADD_TC(tp, recvmmsg_msghdr_heap_after_end);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/secure/fortify_stdio_test.c b/lib/libc/tests/secure/fortify_stdio_test.c
new file mode 100644
index 000000000000..17842393a740
--- /dev/null
+++ b/lib/libc/tests/secure/fortify_stdio_test.c
@@ -0,0 +1,1559 @@
+/* @generated by `generate-fortify-tests.lua "stdio"` */
+
+#define _FORTIFY_SOURCE 2
+#define TMPFILE_SIZE (1024 * 32)
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/random.h>
+#include <sys/resource.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <atf-c.h>
+
+static FILE * __unused
+new_fp(size_t __len)
+{
+ static char fpbuf[LINE_MAX];
+ FILE *fp;
+
+ ATF_REQUIRE(__len <= sizeof(fpbuf));
+
+ memset(fpbuf, 'A', sizeof(fpbuf) - 1);
+ fpbuf[sizeof(fpbuf) - 1] = '\0';
+
+ fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
+ ATF_REQUIRE(fp != NULL);
+
+ return (fp);
+}
+
+/*
+ * Create a new symlink to use for readlink(2) style tests, we'll just use a
+ * random target name to have something interesting to look at.
+ */
+static const char * __unused
+new_symlink(size_t __len)
+{
+ static const char linkname[] = "link";
+ char target[MAXNAMLEN];
+ int error;
+
+ ATF_REQUIRE(__len <= sizeof(target));
+
+ arc4random_buf(target, sizeof(target));
+
+ error = unlink(linkname);
+ ATF_REQUIRE(error == 0 || errno == ENOENT);
+
+ error = symlink(target, linkname);
+ ATF_REQUIRE(error == 0);
+
+ return (linkname);
+}
+
+/*
+ * For our purposes, first descriptor will be the reader; we'll send both
+ * raw data and a control message over it so that the result can be used for
+ * any of our recv*() tests.
+ */
+static void __unused
+new_socket(int sock[2])
+{
+ unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 };
+ static char sockbuf[256];
+ ssize_t rv;
+ size_t total = 0;
+ struct msghdr hdr = { 0 };
+ struct cmsghdr *cmsg;
+ int error, fd;
+
+ error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
+ ATF_REQUIRE(error == 0);
+
+ while (total != sizeof(sockbuf)) {
+ rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0);
+
+ ATF_REQUIRE_MSG(rv > 0,
+ "expected bytes sent, got %zd with %zu left (size %zu, total %zu)",
+ rv, sizeof(sockbuf) - total, sizeof(sockbuf), total);
+ ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf),
+ "%zd exceeds total %zu", rv, sizeof(sockbuf));
+ total += rv;
+ }
+
+ hdr.msg_control = ctrl;
+ hdr.msg_controllen = sizeof(ctrl);
+
+ cmsg = CMSG_FIRSTHDR(&hdr);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ fd = STDIN_FILENO;
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
+
+ error = sendmsg(sock[1], &hdr, 0);
+ ATF_REQUIRE(error != -1);
+}
+
+/*
+ * Constructs a tmpfile that we can use for testing read(2) and friends.
+ */
+static int __unused
+new_tmpfile(void)
+{
+ char buf[1024];
+ ssize_t rv;
+ size_t written;
+ int fd;
+
+ fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
+ ATF_REQUIRE(fd >= 0);
+
+ written = 0;
+ while (written < TMPFILE_SIZE) {
+ rv = write(fd, buf, sizeof(buf));
+ ATF_REQUIRE(rv > 0);
+
+ written += rv;
+ }
+
+ ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
+ return (fd);
+}
+
+static void
+disable_coredumps(void)
+{
+ struct rlimit rl = { 0 };
+
+ if (setrlimit(RLIMIT_CORE, &rl) == -1)
+ _exit(EX_OSERR);
+}
+
+/*
+ * Replaces stdin with a file that we can actually read from, for tests where
+ * we want a FILE * or fd that we can get data from.
+ */
+static void __unused
+replace_stdin(void)
+{
+ int fd;
+
+ fd = new_tmpfile();
+
+ (void)dup2(fd, STDIN_FILENO);
+ if (fd != STDIN_FILENO)
+ close(fd);
+}
+
+ATF_TC(ctermid_before_end);
+ATF_TC_HEAD(ctermid_before_end, tc)
+{
+}
+ATF_TC_BODY(ctermid_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[L_ctermid + 1];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = L_ctermid + 1;
+ const size_t __idx __unused = __len - 1;
+
+ ctermid(__stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(ctermid_end);
+ATF_TC_HEAD(ctermid_end, tc)
+{
+}
+ATF_TC_BODY(ctermid_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[L_ctermid];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = L_ctermid;
+ const size_t __idx __unused = __len - 1;
+
+ ctermid(__stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(ctermid_heap_before_end);
+ATF_TC_HEAD(ctermid_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(ctermid_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (L_ctermid + 1);
+ const size_t __len = L_ctermid + 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ ctermid(__stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(ctermid_heap_end);
+ATF_TC_HEAD(ctermid_heap_end, tc)
+{
+}
+ATF_TC_BODY(ctermid_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (L_ctermid);
+ const size_t __len = L_ctermid;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ ctermid(__stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(ctermid_heap_after_end);
+ATF_TC_HEAD(ctermid_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(ctermid_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (L_ctermid - 1);
+ const size_t __len = L_ctermid - 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ ctermid(__stack.__buf);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(ctermid_r_before_end);
+ATF_TC_HEAD(ctermid_r_before_end, tc)
+{
+}
+ATF_TC_BODY(ctermid_r_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[L_ctermid + 1];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = L_ctermid + 1;
+ const size_t __idx __unused = __len - 1;
+
+ ctermid_r(__stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(ctermid_r_end);
+ATF_TC_HEAD(ctermid_r_end, tc)
+{
+}
+ATF_TC_BODY(ctermid_r_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[L_ctermid];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = L_ctermid;
+ const size_t __idx __unused = __len - 1;
+
+ ctermid_r(__stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(ctermid_r_heap_before_end);
+ATF_TC_HEAD(ctermid_r_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(ctermid_r_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (L_ctermid + 1);
+ const size_t __len = L_ctermid + 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ ctermid_r(__stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(ctermid_r_heap_end);
+ATF_TC_HEAD(ctermid_r_heap_end, tc)
+{
+}
+ATF_TC_BODY(ctermid_r_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (L_ctermid);
+ const size_t __len = L_ctermid;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ ctermid_r(__stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(ctermid_r_heap_after_end);
+ATF_TC_HEAD(ctermid_r_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(ctermid_r_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (L_ctermid - 1);
+ const size_t __len = L_ctermid - 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ ctermid_r(__stack.__buf);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(fread_before_end);
+ATF_TC_HEAD(fread_before_end, tc)
+{
+}
+ATF_TC_BODY(fread_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ replace_stdin();
+
+ fread(__stack.__buf, __len, 1, stdin);
+#undef BUF
+
+}
+
+ATF_TC(fread_end);
+ATF_TC_HEAD(fread_end, tc)
+{
+}
+ATF_TC_BODY(fread_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ replace_stdin();
+
+ fread(__stack.__buf, __len, 1, stdin);
+#undef BUF
+
+}
+
+ATF_TC(fread_heap_before_end);
+ATF_TC_HEAD(fread_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(fread_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+ replace_stdin();
+
+ fread(__stack.__buf, __len, 1, stdin);
+#undef BUF
+
+}
+
+ATF_TC(fread_heap_end);
+ATF_TC_HEAD(fread_heap_end, tc)
+{
+}
+ATF_TC_BODY(fread_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+ replace_stdin();
+
+ fread(__stack.__buf, __len, 1, stdin);
+#undef BUF
+
+}
+
+ATF_TC(fread_heap_after_end);
+ATF_TC_HEAD(fread_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(fread_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ replace_stdin();
+
+ fread(__stack.__buf, __len, 1, stdin);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(fread_unlocked_before_end);
+ATF_TC_HEAD(fread_unlocked_before_end, tc)
+{
+}
+ATF_TC_BODY(fread_unlocked_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ replace_stdin();
+
+ fread_unlocked(__stack.__buf, __len, 1, stdin);
+#undef BUF
+
+}
+
+ATF_TC(fread_unlocked_end);
+ATF_TC_HEAD(fread_unlocked_end, tc)
+{
+}
+ATF_TC_BODY(fread_unlocked_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ replace_stdin();
+
+ fread_unlocked(__stack.__buf, __len, 1, stdin);
+#undef BUF
+
+}
+
+ATF_TC(fread_unlocked_heap_before_end);
+ATF_TC_HEAD(fread_unlocked_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(fread_unlocked_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+ replace_stdin();
+
+ fread_unlocked(__stack.__buf, __len, 1, stdin);
+#undef BUF
+
+}
+
+ATF_TC(fread_unlocked_heap_end);
+ATF_TC_HEAD(fread_unlocked_heap_end, tc)
+{
+}
+ATF_TC_BODY(fread_unlocked_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+ replace_stdin();
+
+ fread_unlocked(__stack.__buf, __len, 1, stdin);
+#undef BUF
+
+}
+
+ATF_TC(fread_unlocked_heap_after_end);
+ATF_TC_HEAD(fread_unlocked_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(fread_unlocked_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ replace_stdin();
+
+ fread_unlocked(__stack.__buf, __len, 1, stdin);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(gets_s_before_end);
+ATF_TC_HEAD(gets_s_before_end, tc)
+{
+}
+ATF_TC_BODY(gets_s_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ replace_stdin();
+
+ gets_s(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(gets_s_end);
+ATF_TC_HEAD(gets_s_end, tc)
+{
+}
+ATF_TC_BODY(gets_s_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ replace_stdin();
+
+ gets_s(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(gets_s_heap_before_end);
+ATF_TC_HEAD(gets_s_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(gets_s_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+ replace_stdin();
+
+ gets_s(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(gets_s_heap_end);
+ATF_TC_HEAD(gets_s_heap_end, tc)
+{
+}
+ATF_TC_BODY(gets_s_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+ replace_stdin();
+
+ gets_s(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(gets_s_heap_after_end);
+ATF_TC_HEAD(gets_s_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(gets_s_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ replace_stdin();
+
+ gets_s(__stack.__buf, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(sprintf_before_end);
+ATF_TC_HEAD(sprintf_before_end, tc)
+{
+}
+ATF_TC_BODY(sprintf_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char srcvar[__len + 10];
+
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ sprintf(__stack.__buf, "%.*s", (int)__len - 1, srcvar);
+#undef BUF
+
+}
+
+ATF_TC(sprintf_end);
+ATF_TC_HEAD(sprintf_end, tc)
+{
+}
+ATF_TC_BODY(sprintf_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char srcvar[__len + 10];
+
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ sprintf(__stack.__buf, "%.*s", (int)__len - 1, srcvar);
+#undef BUF
+
+}
+
+ATF_TC(sprintf_heap_before_end);
+ATF_TC_HEAD(sprintf_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(sprintf_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char srcvar[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ sprintf(__stack.__buf, "%.*s", (int)__len - 1, srcvar);
+#undef BUF
+
+}
+
+ATF_TC(sprintf_heap_end);
+ATF_TC_HEAD(sprintf_heap_end, tc)
+{
+}
+ATF_TC_BODY(sprintf_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char srcvar[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ sprintf(__stack.__buf, "%.*s", (int)__len - 1, srcvar);
+#undef BUF
+
+}
+
+ATF_TC(sprintf_heap_after_end);
+ATF_TC_HEAD(sprintf_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(sprintf_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ char srcvar[__len + 10];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ sprintf(__stack.__buf, "%.*s", (int)__len - 1, srcvar);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(snprintf_before_end);
+ATF_TC_HEAD(snprintf_before_end, tc)
+{
+}
+ATF_TC_BODY(snprintf_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char srcvar[__len + 10];
+
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ snprintf(__stack.__buf, __len, "%.*s", (int)__len - 1, srcvar);
+#undef BUF
+
+}
+
+ATF_TC(snprintf_end);
+ATF_TC_HEAD(snprintf_end, tc)
+{
+}
+ATF_TC_BODY(snprintf_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char srcvar[__len + 10];
+
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ snprintf(__stack.__buf, __len, "%.*s", (int)__len - 1, srcvar);
+#undef BUF
+
+}
+
+ATF_TC(snprintf_heap_before_end);
+ATF_TC_HEAD(snprintf_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(snprintf_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char srcvar[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ snprintf(__stack.__buf, __len, "%.*s", (int)__len - 1, srcvar);
+#undef BUF
+
+}
+
+ATF_TC(snprintf_heap_end);
+ATF_TC_HEAD(snprintf_heap_end, tc)
+{
+}
+ATF_TC_BODY(snprintf_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char srcvar[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ snprintf(__stack.__buf, __len, "%.*s", (int)__len - 1, srcvar);
+#undef BUF
+
+}
+
+ATF_TC(snprintf_heap_after_end);
+ATF_TC_HEAD(snprintf_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(snprintf_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ char srcvar[__len + 10];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+
+ snprintf(__stack.__buf, __len, "%.*s", (int)__len - 1, srcvar);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(tmpnam_before_end);
+ATF_TC_HEAD(tmpnam_before_end, tc)
+{
+}
+ATF_TC_BODY(tmpnam_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[L_tmpnam + 1];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = L_tmpnam + 1;
+ const size_t __idx __unused = __len - 1;
+
+ tmpnam(__stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(tmpnam_end);
+ATF_TC_HEAD(tmpnam_end, tc)
+{
+}
+ATF_TC_BODY(tmpnam_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[L_tmpnam];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = L_tmpnam;
+ const size_t __idx __unused = __len - 1;
+
+ tmpnam(__stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(tmpnam_heap_before_end);
+ATF_TC_HEAD(tmpnam_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(tmpnam_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (L_tmpnam + 1);
+ const size_t __len = L_tmpnam + 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ tmpnam(__stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(tmpnam_heap_end);
+ATF_TC_HEAD(tmpnam_heap_end, tc)
+{
+}
+ATF_TC_BODY(tmpnam_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (L_tmpnam);
+ const size_t __len = L_tmpnam;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ tmpnam(__stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(tmpnam_heap_after_end);
+ATF_TC_HEAD(tmpnam_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(tmpnam_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (L_tmpnam - 1);
+ const size_t __len = L_tmpnam - 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ tmpnam(__stack.__buf);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(fgets_before_end);
+ATF_TC_HEAD(fgets_before_end, tc)
+{
+}
+ATF_TC_BODY(fgets_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ FILE *fp;
+
+ fp = new_fp(__len);
+
+ fgets(__stack.__buf, __len, fp);
+#undef BUF
+
+}
+
+ATF_TC(fgets_end);
+ATF_TC_HEAD(fgets_end, tc)
+{
+}
+ATF_TC_BODY(fgets_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ FILE *fp;
+
+ fp = new_fp(__len);
+
+ fgets(__stack.__buf, __len, fp);
+#undef BUF
+
+}
+
+ATF_TC(fgets_heap_before_end);
+ATF_TC_HEAD(fgets_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(fgets_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ FILE *fp;
+
+ __stack.__buf = malloc(__bufsz);
+ fp = new_fp(__len);
+
+ fgets(__stack.__buf, __len, fp);
+#undef BUF
+
+}
+
+ATF_TC(fgets_heap_end);
+ATF_TC_HEAD(fgets_heap_end, tc)
+{
+}
+ATF_TC_BODY(fgets_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ FILE *fp;
+
+ __stack.__buf = malloc(__bufsz);
+ fp = new_fp(__len);
+
+ fgets(__stack.__buf, __len, fp);
+#undef BUF
+
+}
+
+ATF_TC(fgets_heap_after_end);
+ATF_TC_HEAD(fgets_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(fgets_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ FILE *fp;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ fp = new_fp(__len);
+
+ fgets(__stack.__buf, __len, fp);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, ctermid_before_end);
+ ATF_TP_ADD_TC(tp, ctermid_end);
+ ATF_TP_ADD_TC(tp, ctermid_heap_before_end);
+ ATF_TP_ADD_TC(tp, ctermid_heap_end);
+ ATF_TP_ADD_TC(tp, ctermid_heap_after_end);
+ ATF_TP_ADD_TC(tp, ctermid_r_before_end);
+ ATF_TP_ADD_TC(tp, ctermid_r_end);
+ ATF_TP_ADD_TC(tp, ctermid_r_heap_before_end);
+ ATF_TP_ADD_TC(tp, ctermid_r_heap_end);
+ ATF_TP_ADD_TC(tp, ctermid_r_heap_after_end);
+ ATF_TP_ADD_TC(tp, fread_before_end);
+ ATF_TP_ADD_TC(tp, fread_end);
+ ATF_TP_ADD_TC(tp, fread_heap_before_end);
+ ATF_TP_ADD_TC(tp, fread_heap_end);
+ ATF_TP_ADD_TC(tp, fread_heap_after_end);
+ ATF_TP_ADD_TC(tp, fread_unlocked_before_end);
+ ATF_TP_ADD_TC(tp, fread_unlocked_end);
+ ATF_TP_ADD_TC(tp, fread_unlocked_heap_before_end);
+ ATF_TP_ADD_TC(tp, fread_unlocked_heap_end);
+ ATF_TP_ADD_TC(tp, fread_unlocked_heap_after_end);
+ ATF_TP_ADD_TC(tp, gets_s_before_end);
+ ATF_TP_ADD_TC(tp, gets_s_end);
+ ATF_TP_ADD_TC(tp, gets_s_heap_before_end);
+ ATF_TP_ADD_TC(tp, gets_s_heap_end);
+ ATF_TP_ADD_TC(tp, gets_s_heap_after_end);
+ ATF_TP_ADD_TC(tp, sprintf_before_end);
+ ATF_TP_ADD_TC(tp, sprintf_end);
+ ATF_TP_ADD_TC(tp, sprintf_heap_before_end);
+ ATF_TP_ADD_TC(tp, sprintf_heap_end);
+ ATF_TP_ADD_TC(tp, sprintf_heap_after_end);
+ ATF_TP_ADD_TC(tp, snprintf_before_end);
+ ATF_TP_ADD_TC(tp, snprintf_end);
+ ATF_TP_ADD_TC(tp, snprintf_heap_before_end);
+ ATF_TP_ADD_TC(tp, snprintf_heap_end);
+ ATF_TP_ADD_TC(tp, snprintf_heap_after_end);
+ ATF_TP_ADD_TC(tp, tmpnam_before_end);
+ ATF_TP_ADD_TC(tp, tmpnam_end);
+ ATF_TP_ADD_TC(tp, tmpnam_heap_before_end);
+ ATF_TP_ADD_TC(tp, tmpnam_heap_end);
+ ATF_TP_ADD_TC(tp, tmpnam_heap_after_end);
+ ATF_TP_ADD_TC(tp, fgets_before_end);
+ ATF_TP_ADD_TC(tp, fgets_end);
+ ATF_TP_ADD_TC(tp, fgets_heap_before_end);
+ ATF_TP_ADD_TC(tp, fgets_heap_end);
+ ATF_TP_ADD_TC(tp, fgets_heap_after_end);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/secure/fortify_stdlib_test.c b/lib/libc/tests/secure/fortify_stdlib_test.c
new file mode 100644
index 000000000000..d0b1af78da86
--- /dev/null
+++ b/lib/libc/tests/secure/fortify_stdlib_test.c
@@ -0,0 +1,610 @@
+/* @generated by `generate-fortify-tests.lua "stdlib"` */
+
+#define _FORTIFY_SOURCE 2
+#define TMPFILE_SIZE (1024 * 32)
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/random.h>
+#include <sys/resource.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <atf-c.h>
+
+static FILE * __unused
+new_fp(size_t __len)
+{
+ static char fpbuf[LINE_MAX];
+ FILE *fp;
+
+ ATF_REQUIRE(__len <= sizeof(fpbuf));
+
+ memset(fpbuf, 'A', sizeof(fpbuf) - 1);
+ fpbuf[sizeof(fpbuf) - 1] = '\0';
+
+ fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
+ ATF_REQUIRE(fp != NULL);
+
+ return (fp);
+}
+
+/*
+ * Create a new symlink to use for readlink(2) style tests, we'll just use a
+ * random target name to have something interesting to look at.
+ */
+static const char * __unused
+new_symlink(size_t __len)
+{
+ static const char linkname[] = "link";
+ char target[MAXNAMLEN];
+ int error;
+
+ ATF_REQUIRE(__len <= sizeof(target));
+
+ arc4random_buf(target, sizeof(target));
+
+ error = unlink(linkname);
+ ATF_REQUIRE(error == 0 || errno == ENOENT);
+
+ error = symlink(target, linkname);
+ ATF_REQUIRE(error == 0);
+
+ return (linkname);
+}
+
+/*
+ * For our purposes, first descriptor will be the reader; we'll send both
+ * raw data and a control message over it so that the result can be used for
+ * any of our recv*() tests.
+ */
+static void __unused
+new_socket(int sock[2])
+{
+ unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 };
+ static char sockbuf[256];
+ ssize_t rv;
+ size_t total = 0;
+ struct msghdr hdr = { 0 };
+ struct cmsghdr *cmsg;
+ int error, fd;
+
+ error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
+ ATF_REQUIRE(error == 0);
+
+ while (total != sizeof(sockbuf)) {
+ rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0);
+
+ ATF_REQUIRE_MSG(rv > 0,
+ "expected bytes sent, got %zd with %zu left (size %zu, total %zu)",
+ rv, sizeof(sockbuf) - total, sizeof(sockbuf), total);
+ ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf),
+ "%zd exceeds total %zu", rv, sizeof(sockbuf));
+ total += rv;
+ }
+
+ hdr.msg_control = ctrl;
+ hdr.msg_controllen = sizeof(ctrl);
+
+ cmsg = CMSG_FIRSTHDR(&hdr);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ fd = STDIN_FILENO;
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
+
+ error = sendmsg(sock[1], &hdr, 0);
+ ATF_REQUIRE(error != -1);
+}
+
+/*
+ * Constructs a tmpfile that we can use for testing read(2) and friends.
+ */
+static int __unused
+new_tmpfile(void)
+{
+ char buf[1024];
+ ssize_t rv;
+ size_t written;
+ int fd;
+
+ fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
+ ATF_REQUIRE(fd >= 0);
+
+ written = 0;
+ while (written < TMPFILE_SIZE) {
+ rv = write(fd, buf, sizeof(buf));
+ ATF_REQUIRE(rv > 0);
+
+ written += rv;
+ }
+
+ ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
+ return (fd);
+}
+
+static void
+disable_coredumps(void)
+{
+ struct rlimit rl = { 0 };
+
+ if (setrlimit(RLIMIT_CORE, &rl) == -1)
+ _exit(EX_OSERR);
+}
+
+/*
+ * Replaces stdin with a file that we can actually read from, for tests where
+ * we want a FILE * or fd that we can get data from.
+ */
+static void __unused
+replace_stdin(void)
+{
+ int fd;
+
+ fd = new_tmpfile();
+
+ (void)dup2(fd, STDIN_FILENO);
+ if (fd != STDIN_FILENO)
+ close(fd);
+}
+
+ATF_TC(arc4random_buf_before_end);
+ATF_TC_HEAD(arc4random_buf_before_end, tc)
+{
+}
+ATF_TC_BODY(arc4random_buf_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ arc4random_buf(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(arc4random_buf_end);
+ATF_TC_HEAD(arc4random_buf_end, tc)
+{
+}
+ATF_TC_BODY(arc4random_buf_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ arc4random_buf(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(arc4random_buf_heap_before_end);
+ATF_TC_HEAD(arc4random_buf_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(arc4random_buf_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ arc4random_buf(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(arc4random_buf_heap_end);
+ATF_TC_HEAD(arc4random_buf_heap_end, tc)
+{
+}
+ATF_TC_BODY(arc4random_buf_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ arc4random_buf(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(arc4random_buf_heap_after_end);
+ATF_TC_HEAD(arc4random_buf_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(arc4random_buf_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ arc4random_buf(__stack.__buf, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(getenv_r_before_end);
+ATF_TC_HEAD(getenv_r_before_end, tc)
+{
+}
+ATF_TC_BODY(getenv_r_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ getenv_r("PATH", __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getenv_r_end);
+ATF_TC_HEAD(getenv_r_end, tc)
+{
+}
+ATF_TC_BODY(getenv_r_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ getenv_r("PATH", __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getenv_r_heap_before_end);
+ATF_TC_HEAD(getenv_r_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(getenv_r_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ getenv_r("PATH", __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getenv_r_heap_end);
+ATF_TC_HEAD(getenv_r_heap_end, tc)
+{
+}
+ATF_TC_BODY(getenv_r_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ getenv_r("PATH", __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getenv_r_heap_after_end);
+ATF_TC_HEAD(getenv_r_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(getenv_r_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ getenv_r("PATH", __stack.__buf, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(realpath_before_end);
+ATF_TC_HEAD(realpath_before_end, tc)
+{
+}
+ATF_TC_BODY(realpath_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[PATH_MAX + 1];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = PATH_MAX + 1;
+ const size_t __idx __unused = __len - 1;
+
+ realpath(".", __stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(realpath_end);
+ATF_TC_HEAD(realpath_end, tc)
+{
+}
+ATF_TC_BODY(realpath_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[PATH_MAX];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = PATH_MAX;
+ const size_t __idx __unused = __len - 1;
+
+ realpath(".", __stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(realpath_heap_before_end);
+ATF_TC_HEAD(realpath_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(realpath_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (PATH_MAX + 1);
+ const size_t __len = PATH_MAX + 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ realpath(".", __stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(realpath_heap_end);
+ATF_TC_HEAD(realpath_heap_end, tc)
+{
+}
+ATF_TC_BODY(realpath_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (PATH_MAX);
+ const size_t __len = PATH_MAX;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ realpath(".", __stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(realpath_heap_after_end);
+ATF_TC_HEAD(realpath_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(realpath_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (PATH_MAX - 1);
+ const size_t __len = PATH_MAX - 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ realpath(".", __stack.__buf);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, arc4random_buf_before_end);
+ ATF_TP_ADD_TC(tp, arc4random_buf_end);
+ ATF_TP_ADD_TC(tp, arc4random_buf_heap_before_end);
+ ATF_TP_ADD_TC(tp, arc4random_buf_heap_end);
+ ATF_TP_ADD_TC(tp, arc4random_buf_heap_after_end);
+ ATF_TP_ADD_TC(tp, getenv_r_before_end);
+ ATF_TP_ADD_TC(tp, getenv_r_end);
+ ATF_TP_ADD_TC(tp, getenv_r_heap_before_end);
+ ATF_TP_ADD_TC(tp, getenv_r_heap_end);
+ ATF_TP_ADD_TC(tp, getenv_r_heap_after_end);
+ ATF_TP_ADD_TC(tp, realpath_before_end);
+ ATF_TP_ADD_TC(tp, realpath_end);
+ ATF_TP_ADD_TC(tp, realpath_heap_before_end);
+ ATF_TP_ADD_TC(tp, realpath_heap_end);
+ ATF_TP_ADD_TC(tp, realpath_heap_after_end);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/secure/fortify_string_test.c b/lib/libc/tests/secure/fortify_string_test.c
new file mode 100644
index 000000000000..cfea261ff66f
--- /dev/null
+++ b/lib/libc/tests/secure/fortify_string_test.c
@@ -0,0 +1,2271 @@
+/* @generated by `generate-fortify-tests.lua "string"` */
+
+#define _FORTIFY_SOURCE 2
+#define TMPFILE_SIZE (1024 * 32)
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/random.h>
+#include <sys/resource.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <atf-c.h>
+
+static FILE * __unused
+new_fp(size_t __len)
+{
+ static char fpbuf[LINE_MAX];
+ FILE *fp;
+
+ ATF_REQUIRE(__len <= sizeof(fpbuf));
+
+ memset(fpbuf, 'A', sizeof(fpbuf) - 1);
+ fpbuf[sizeof(fpbuf) - 1] = '\0';
+
+ fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
+ ATF_REQUIRE(fp != NULL);
+
+ return (fp);
+}
+
+/*
+ * Create a new symlink to use for readlink(2) style tests, we'll just use a
+ * random target name to have something interesting to look at.
+ */
+static const char * __unused
+new_symlink(size_t __len)
+{
+ static const char linkname[] = "link";
+ char target[MAXNAMLEN];
+ int error;
+
+ ATF_REQUIRE(__len <= sizeof(target));
+
+ arc4random_buf(target, sizeof(target));
+
+ error = unlink(linkname);
+ ATF_REQUIRE(error == 0 || errno == ENOENT);
+
+ error = symlink(target, linkname);
+ ATF_REQUIRE(error == 0);
+
+ return (linkname);
+}
+
+/*
+ * For our purposes, first descriptor will be the reader; we'll send both
+ * raw data and a control message over it so that the result can be used for
+ * any of our recv*() tests.
+ */
+static void __unused
+new_socket(int sock[2])
+{
+ unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 };
+ static char sockbuf[256];
+ ssize_t rv;
+ size_t total = 0;
+ struct msghdr hdr = { 0 };
+ struct cmsghdr *cmsg;
+ int error, fd;
+
+ error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
+ ATF_REQUIRE(error == 0);
+
+ while (total != sizeof(sockbuf)) {
+ rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0);
+
+ ATF_REQUIRE_MSG(rv > 0,
+ "expected bytes sent, got %zd with %zu left (size %zu, total %zu)",
+ rv, sizeof(sockbuf) - total, sizeof(sockbuf), total);
+ ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf),
+ "%zd exceeds total %zu", rv, sizeof(sockbuf));
+ total += rv;
+ }
+
+ hdr.msg_control = ctrl;
+ hdr.msg_controllen = sizeof(ctrl);
+
+ cmsg = CMSG_FIRSTHDR(&hdr);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ fd = STDIN_FILENO;
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
+
+ error = sendmsg(sock[1], &hdr, 0);
+ ATF_REQUIRE(error != -1);
+}
+
+/*
+ * Constructs a tmpfile that we can use for testing read(2) and friends.
+ */
+static int __unused
+new_tmpfile(void)
+{
+ char buf[1024];
+ ssize_t rv;
+ size_t written;
+ int fd;
+
+ fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
+ ATF_REQUIRE(fd >= 0);
+
+ written = 0;
+ while (written < TMPFILE_SIZE) {
+ rv = write(fd, buf, sizeof(buf));
+ ATF_REQUIRE(rv > 0);
+
+ written += rv;
+ }
+
+ ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
+ return (fd);
+}
+
+static void
+disable_coredumps(void)
+{
+ struct rlimit rl = { 0 };
+
+ if (setrlimit(RLIMIT_CORE, &rl) == -1)
+ _exit(EX_OSERR);
+}
+
+/*
+ * Replaces stdin with a file that we can actually read from, for tests where
+ * we want a FILE * or fd that we can get data from.
+ */
+static void __unused
+replace_stdin(void)
+{
+ int fd;
+
+ fd = new_tmpfile();
+
+ (void)dup2(fd, STDIN_FILENO);
+ if (fd != STDIN_FILENO)
+ close(fd);
+}
+
+ATF_TC(memcpy_before_end);
+ATF_TC_HEAD(memcpy_before_end, tc)
+{
+}
+ATF_TC_BODY(memcpy_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len + 10];
+
+ memcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(memcpy_end);
+ATF_TC_HEAD(memcpy_end, tc)
+{
+}
+ATF_TC_BODY(memcpy_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len + 10];
+
+ memcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(memcpy_heap_before_end);
+ATF_TC_HEAD(memcpy_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(memcpy_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ memcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(memcpy_heap_end);
+ATF_TC_HEAD(memcpy_heap_end, tc)
+{
+}
+ATF_TC_BODY(memcpy_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ memcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(memcpy_heap_after_end);
+ATF_TC_HEAD(memcpy_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(memcpy_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ char src[__len + 10];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ memcpy(__stack.__buf, src, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(mempcpy_before_end);
+ATF_TC_HEAD(mempcpy_before_end, tc)
+{
+}
+ATF_TC_BODY(mempcpy_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len + 10];
+
+ mempcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(mempcpy_end);
+ATF_TC_HEAD(mempcpy_end, tc)
+{
+}
+ATF_TC_BODY(mempcpy_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len + 10];
+
+ mempcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(mempcpy_heap_before_end);
+ATF_TC_HEAD(mempcpy_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(mempcpy_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ mempcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(mempcpy_heap_end);
+ATF_TC_HEAD(mempcpy_heap_end, tc)
+{
+}
+ATF_TC_BODY(mempcpy_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ mempcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(mempcpy_heap_after_end);
+ATF_TC_HEAD(mempcpy_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(mempcpy_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ char src[__len + 10];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ mempcpy(__stack.__buf, src, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(memmove_before_end);
+ATF_TC_HEAD(memmove_before_end, tc)
+{
+}
+ATF_TC_BODY(memmove_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len + 10];
+
+ memmove(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(memmove_end);
+ATF_TC_HEAD(memmove_end, tc)
+{
+}
+ATF_TC_BODY(memmove_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len + 10];
+
+ memmove(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(memmove_heap_before_end);
+ATF_TC_HEAD(memmove_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(memmove_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ memmove(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(memmove_heap_end);
+ATF_TC_HEAD(memmove_heap_end, tc)
+{
+}
+ATF_TC_BODY(memmove_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ memmove(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(memmove_heap_after_end);
+ATF_TC_HEAD(memmove_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(memmove_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ char src[__len + 10];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ memmove(__stack.__buf, src, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(memset_before_end);
+ATF_TC_HEAD(memset_before_end, tc)
+{
+}
+ATF_TC_BODY(memset_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ memset(__stack.__buf, 0, __len);
+#undef BUF
+
+}
+
+ATF_TC(memset_end);
+ATF_TC_HEAD(memset_end, tc)
+{
+}
+ATF_TC_BODY(memset_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ memset(__stack.__buf, 0, __len);
+#undef BUF
+
+}
+
+ATF_TC(memset_heap_before_end);
+ATF_TC_HEAD(memset_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(memset_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ memset(__stack.__buf, 0, __len);
+#undef BUF
+
+}
+
+ATF_TC(memset_heap_end);
+ATF_TC_HEAD(memset_heap_end, tc)
+{
+}
+ATF_TC_BODY(memset_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ memset(__stack.__buf, 0, __len);
+#undef BUF
+
+}
+
+ATF_TC(memset_heap_after_end);
+ATF_TC_HEAD(memset_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(memset_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ memset(__stack.__buf, 0, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(memset_explicit_before_end);
+ATF_TC_HEAD(memset_explicit_before_end, tc)
+{
+}
+ATF_TC_BODY(memset_explicit_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ memset_explicit(__stack.__buf, 0, __len);
+#undef BUF
+
+}
+
+ATF_TC(memset_explicit_end);
+ATF_TC_HEAD(memset_explicit_end, tc)
+{
+}
+ATF_TC_BODY(memset_explicit_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ memset_explicit(__stack.__buf, 0, __len);
+#undef BUF
+
+}
+
+ATF_TC(memset_explicit_heap_before_end);
+ATF_TC_HEAD(memset_explicit_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(memset_explicit_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ memset_explicit(__stack.__buf, 0, __len);
+#undef BUF
+
+}
+
+ATF_TC(memset_explicit_heap_end);
+ATF_TC_HEAD(memset_explicit_heap_end, tc)
+{
+}
+ATF_TC_BODY(memset_explicit_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ memset_explicit(__stack.__buf, 0, __len);
+#undef BUF
+
+}
+
+ATF_TC(memset_explicit_heap_after_end);
+ATF_TC_HEAD(memset_explicit_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(memset_explicit_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ memset_explicit(__stack.__buf, 0, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(stpcpy_before_end);
+ATF_TC_HEAD(stpcpy_before_end, tc)
+{
+}
+ATF_TC_BODY(stpcpy_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(stpcpy_end);
+ATF_TC_HEAD(stpcpy_end, tc)
+{
+}
+ATF_TC_BODY(stpcpy_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(stpcpy_heap_before_end);
+ATF_TC_HEAD(stpcpy_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(stpcpy_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(stpcpy_heap_end);
+ATF_TC_HEAD(stpcpy_heap_end, tc)
+{
+}
+ATF_TC_BODY(stpcpy_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(stpcpy_heap_after_end);
+ATF_TC_HEAD(stpcpy_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(stpcpy_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ char src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpcpy(__stack.__buf, src);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(stpncpy_before_end);
+ATF_TC_HEAD(stpncpy_before_end, tc)
+{
+}
+ATF_TC_BODY(stpncpy_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(stpncpy_end);
+ATF_TC_HEAD(stpncpy_end, tc)
+{
+}
+ATF_TC_BODY(stpncpy_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(stpncpy_heap_before_end);
+ATF_TC_HEAD(stpncpy_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(stpncpy_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(stpncpy_heap_end);
+ATF_TC_HEAD(stpncpy_heap_end, tc)
+{
+}
+ATF_TC_BODY(stpncpy_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(stpncpy_heap_after_end);
+ATF_TC_HEAD(stpncpy_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(stpncpy_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ char src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ stpncpy(__stack.__buf, src, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(strcat_before_end);
+ATF_TC_HEAD(strcat_before_end, tc)
+{
+}
+ATF_TC_BODY(strcat_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcat(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(strcat_end);
+ATF_TC_HEAD(strcat_end, tc)
+{
+}
+ATF_TC_BODY(strcat_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcat(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(strcat_heap_before_end);
+ATF_TC_HEAD(strcat_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(strcat_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcat(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(strcat_heap_end);
+ATF_TC_HEAD(strcat_heap_end, tc)
+{
+}
+ATF_TC_BODY(strcat_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcat(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(strcat_heap_after_end);
+ATF_TC_HEAD(strcat_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(strcat_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ char src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcat(__stack.__buf, src);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(strlcat_before_end);
+ATF_TC_HEAD(strlcat_before_end, tc)
+{
+}
+ATF_TC_BODY(strlcat_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strlcat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(strlcat_end);
+ATF_TC_HEAD(strlcat_end, tc)
+{
+}
+ATF_TC_BODY(strlcat_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strlcat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(strlcat_heap_before_end);
+ATF_TC_HEAD(strlcat_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(strlcat_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strlcat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(strlcat_heap_end);
+ATF_TC_HEAD(strlcat_heap_end, tc)
+{
+}
+ATF_TC_BODY(strlcat_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strlcat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(strlcat_heap_after_end);
+ATF_TC_HEAD(strlcat_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(strlcat_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ char src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strlcat(__stack.__buf, src, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(strncat_before_end);
+ATF_TC_HEAD(strncat_before_end, tc)
+{
+}
+ATF_TC_BODY(strncat_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(strncat_end);
+ATF_TC_HEAD(strncat_end, tc)
+{
+}
+ATF_TC_BODY(strncat_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(strncat_heap_before_end);
+ATF_TC_HEAD(strncat_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(strncat_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(strncat_heap_end);
+ATF_TC_HEAD(strncat_heap_end, tc)
+{
+}
+ATF_TC_BODY(strncat_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(strncat_heap_after_end);
+ATF_TC_HEAD(strncat_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(strncat_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ char src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncat(__stack.__buf, src, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(strcpy_before_end);
+ATF_TC_HEAD(strcpy_before_end, tc)
+{
+}
+ATF_TC_BODY(strcpy_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(strcpy_end);
+ATF_TC_HEAD(strcpy_end, tc)
+{
+}
+ATF_TC_BODY(strcpy_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(strcpy_heap_before_end);
+ATF_TC_HEAD(strcpy_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(strcpy_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(strcpy_heap_end);
+ATF_TC_HEAD(strcpy_heap_end, tc)
+{
+}
+ATF_TC_BODY(strcpy_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(strcpy_heap_after_end);
+ATF_TC_HEAD(strcpy_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(strcpy_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ char src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strcpy(__stack.__buf, src);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(strlcpy_before_end);
+ATF_TC_HEAD(strlcpy_before_end, tc)
+{
+}
+ATF_TC_BODY(strlcpy_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strlcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(strlcpy_end);
+ATF_TC_HEAD(strlcpy_end, tc)
+{
+}
+ATF_TC_BODY(strlcpy_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strlcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(strlcpy_heap_before_end);
+ATF_TC_HEAD(strlcpy_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(strlcpy_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strlcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(strlcpy_heap_end);
+ATF_TC_HEAD(strlcpy_heap_end, tc)
+{
+}
+ATF_TC_BODY(strlcpy_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strlcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(strlcpy_heap_after_end);
+ATF_TC_HEAD(strlcpy_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(strlcpy_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ char src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strlcpy(__stack.__buf, src, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(strncpy_before_end);
+ATF_TC_HEAD(strncpy_before_end, tc)
+{
+}
+ATF_TC_BODY(strncpy_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(strncpy_end);
+ATF_TC_HEAD(strncpy_end, tc)
+{
+}
+ATF_TC_BODY(strncpy_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(strncpy_heap_before_end);
+ATF_TC_HEAD(strncpy_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(strncpy_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(strncpy_heap_end);
+ATF_TC_HEAD(strncpy_heap_end, tc)
+{
+}
+ATF_TC_BODY(strncpy_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(strncpy_heap_after_end);
+ATF_TC_HEAD(strncpy_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(strncpy_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ char src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ strncpy(__stack.__buf, src, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, memcpy_before_end);
+ ATF_TP_ADD_TC(tp, memcpy_end);
+ ATF_TP_ADD_TC(tp, memcpy_heap_before_end);
+ ATF_TP_ADD_TC(tp, memcpy_heap_end);
+ ATF_TP_ADD_TC(tp, memcpy_heap_after_end);
+ ATF_TP_ADD_TC(tp, mempcpy_before_end);
+ ATF_TP_ADD_TC(tp, mempcpy_end);
+ ATF_TP_ADD_TC(tp, mempcpy_heap_before_end);
+ ATF_TP_ADD_TC(tp, mempcpy_heap_end);
+ ATF_TP_ADD_TC(tp, mempcpy_heap_after_end);
+ ATF_TP_ADD_TC(tp, memmove_before_end);
+ ATF_TP_ADD_TC(tp, memmove_end);
+ ATF_TP_ADD_TC(tp, memmove_heap_before_end);
+ ATF_TP_ADD_TC(tp, memmove_heap_end);
+ ATF_TP_ADD_TC(tp, memmove_heap_after_end);
+ ATF_TP_ADD_TC(tp, memset_before_end);
+ ATF_TP_ADD_TC(tp, memset_end);
+ ATF_TP_ADD_TC(tp, memset_heap_before_end);
+ ATF_TP_ADD_TC(tp, memset_heap_end);
+ ATF_TP_ADD_TC(tp, memset_heap_after_end);
+ ATF_TP_ADD_TC(tp, memset_explicit_before_end);
+ ATF_TP_ADD_TC(tp, memset_explicit_end);
+ ATF_TP_ADD_TC(tp, memset_explicit_heap_before_end);
+ ATF_TP_ADD_TC(tp, memset_explicit_heap_end);
+ ATF_TP_ADD_TC(tp, memset_explicit_heap_after_end);
+ ATF_TP_ADD_TC(tp, stpcpy_before_end);
+ ATF_TP_ADD_TC(tp, stpcpy_end);
+ ATF_TP_ADD_TC(tp, stpcpy_heap_before_end);
+ ATF_TP_ADD_TC(tp, stpcpy_heap_end);
+ ATF_TP_ADD_TC(tp, stpcpy_heap_after_end);
+ ATF_TP_ADD_TC(tp, stpncpy_before_end);
+ ATF_TP_ADD_TC(tp, stpncpy_end);
+ ATF_TP_ADD_TC(tp, stpncpy_heap_before_end);
+ ATF_TP_ADD_TC(tp, stpncpy_heap_end);
+ ATF_TP_ADD_TC(tp, stpncpy_heap_after_end);
+ ATF_TP_ADD_TC(tp, strcat_before_end);
+ ATF_TP_ADD_TC(tp, strcat_end);
+ ATF_TP_ADD_TC(tp, strcat_heap_before_end);
+ ATF_TP_ADD_TC(tp, strcat_heap_end);
+ ATF_TP_ADD_TC(tp, strcat_heap_after_end);
+ ATF_TP_ADD_TC(tp, strlcat_before_end);
+ ATF_TP_ADD_TC(tp, strlcat_end);
+ ATF_TP_ADD_TC(tp, strlcat_heap_before_end);
+ ATF_TP_ADD_TC(tp, strlcat_heap_end);
+ ATF_TP_ADD_TC(tp, strlcat_heap_after_end);
+ ATF_TP_ADD_TC(tp, strncat_before_end);
+ ATF_TP_ADD_TC(tp, strncat_end);
+ ATF_TP_ADD_TC(tp, strncat_heap_before_end);
+ ATF_TP_ADD_TC(tp, strncat_heap_end);
+ ATF_TP_ADD_TC(tp, strncat_heap_after_end);
+ ATF_TP_ADD_TC(tp, strcpy_before_end);
+ ATF_TP_ADD_TC(tp, strcpy_end);
+ ATF_TP_ADD_TC(tp, strcpy_heap_before_end);
+ ATF_TP_ADD_TC(tp, strcpy_heap_end);
+ ATF_TP_ADD_TC(tp, strcpy_heap_after_end);
+ ATF_TP_ADD_TC(tp, strlcpy_before_end);
+ ATF_TP_ADD_TC(tp, strlcpy_end);
+ ATF_TP_ADD_TC(tp, strlcpy_heap_before_end);
+ ATF_TP_ADD_TC(tp, strlcpy_heap_end);
+ ATF_TP_ADD_TC(tp, strlcpy_heap_after_end);
+ ATF_TP_ADD_TC(tp, strncpy_before_end);
+ ATF_TP_ADD_TC(tp, strncpy_end);
+ ATF_TP_ADD_TC(tp, strncpy_heap_before_end);
+ ATF_TP_ADD_TC(tp, strncpy_heap_end);
+ ATF_TP_ADD_TC(tp, strncpy_heap_after_end);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/secure/fortify_strings_test.c b/lib/libc/tests/secure/fortify_strings_test.c
new file mode 100644
index 000000000000..9f7d37a2480e
--- /dev/null
+++ b/lib/libc/tests/secure/fortify_strings_test.c
@@ -0,0 +1,615 @@
+/* @generated by `generate-fortify-tests.lua "strings"` */
+
+#define _FORTIFY_SOURCE 2
+#define TMPFILE_SIZE (1024 * 32)
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/random.h>
+#include <sys/resource.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <atf-c.h>
+
+static FILE * __unused
+new_fp(size_t __len)
+{
+ static char fpbuf[LINE_MAX];
+ FILE *fp;
+
+ ATF_REQUIRE(__len <= sizeof(fpbuf));
+
+ memset(fpbuf, 'A', sizeof(fpbuf) - 1);
+ fpbuf[sizeof(fpbuf) - 1] = '\0';
+
+ fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
+ ATF_REQUIRE(fp != NULL);
+
+ return (fp);
+}
+
+/*
+ * Create a new symlink to use for readlink(2) style tests, we'll just use a
+ * random target name to have something interesting to look at.
+ */
+static const char * __unused
+new_symlink(size_t __len)
+{
+ static const char linkname[] = "link";
+ char target[MAXNAMLEN];
+ int error;
+
+ ATF_REQUIRE(__len <= sizeof(target));
+
+ arc4random_buf(target, sizeof(target));
+
+ error = unlink(linkname);
+ ATF_REQUIRE(error == 0 || errno == ENOENT);
+
+ error = symlink(target, linkname);
+ ATF_REQUIRE(error == 0);
+
+ return (linkname);
+}
+
+/*
+ * For our purposes, first descriptor will be the reader; we'll send both
+ * raw data and a control message over it so that the result can be used for
+ * any of our recv*() tests.
+ */
+static void __unused
+new_socket(int sock[2])
+{
+ unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 };
+ static char sockbuf[256];
+ ssize_t rv;
+ size_t total = 0;
+ struct msghdr hdr = { 0 };
+ struct cmsghdr *cmsg;
+ int error, fd;
+
+ error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
+ ATF_REQUIRE(error == 0);
+
+ while (total != sizeof(sockbuf)) {
+ rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0);
+
+ ATF_REQUIRE_MSG(rv > 0,
+ "expected bytes sent, got %zd with %zu left (size %zu, total %zu)",
+ rv, sizeof(sockbuf) - total, sizeof(sockbuf), total);
+ ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf),
+ "%zd exceeds total %zu", rv, sizeof(sockbuf));
+ total += rv;
+ }
+
+ hdr.msg_control = ctrl;
+ hdr.msg_controllen = sizeof(ctrl);
+
+ cmsg = CMSG_FIRSTHDR(&hdr);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ fd = STDIN_FILENO;
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
+
+ error = sendmsg(sock[1], &hdr, 0);
+ ATF_REQUIRE(error != -1);
+}
+
+/*
+ * Constructs a tmpfile that we can use for testing read(2) and friends.
+ */
+static int __unused
+new_tmpfile(void)
+{
+ char buf[1024];
+ ssize_t rv;
+ size_t written;
+ int fd;
+
+ fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
+ ATF_REQUIRE(fd >= 0);
+
+ written = 0;
+ while (written < TMPFILE_SIZE) {
+ rv = write(fd, buf, sizeof(buf));
+ ATF_REQUIRE(rv > 0);
+
+ written += rv;
+ }
+
+ ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
+ return (fd);
+}
+
+static void
+disable_coredumps(void)
+{
+ struct rlimit rl = { 0 };
+
+ if (setrlimit(RLIMIT_CORE, &rl) == -1)
+ _exit(EX_OSERR);
+}
+
+/*
+ * Replaces stdin with a file that we can actually read from, for tests where
+ * we want a FILE * or fd that we can get data from.
+ */
+static void __unused
+replace_stdin(void)
+{
+ int fd;
+
+ fd = new_tmpfile();
+
+ (void)dup2(fd, STDIN_FILENO);
+ if (fd != STDIN_FILENO)
+ close(fd);
+}
+
+ATF_TC(bcopy_before_end);
+ATF_TC_HEAD(bcopy_before_end, tc)
+{
+}
+ATF_TC_BODY(bcopy_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len + 10];
+
+ bcopy(src, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(bcopy_end);
+ATF_TC_HEAD(bcopy_end, tc)
+{
+}
+ATF_TC_BODY(bcopy_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len + 10];
+
+ bcopy(src, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(bcopy_heap_before_end);
+ATF_TC_HEAD(bcopy_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(bcopy_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ char src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ bcopy(src, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(bcopy_heap_end);
+ATF_TC_HEAD(bcopy_heap_end, tc)
+{
+}
+ATF_TC_BODY(bcopy_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ char src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ bcopy(src, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(bcopy_heap_after_end);
+ATF_TC_HEAD(bcopy_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(bcopy_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ char src[__len + 10];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ bcopy(src, __stack.__buf, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(bzero_before_end);
+ATF_TC_HEAD(bzero_before_end, tc)
+{
+}
+ATF_TC_BODY(bzero_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ bzero(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(bzero_end);
+ATF_TC_HEAD(bzero_end, tc)
+{
+}
+ATF_TC_BODY(bzero_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ bzero(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(bzero_heap_before_end);
+ATF_TC_HEAD(bzero_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(bzero_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ bzero(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(bzero_heap_end);
+ATF_TC_HEAD(bzero_heap_end, tc)
+{
+}
+ATF_TC_BODY(bzero_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ bzero(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(bzero_heap_after_end);
+ATF_TC_HEAD(bzero_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(bzero_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ bzero(__stack.__buf, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(explicit_bzero_before_end);
+ATF_TC_HEAD(explicit_bzero_before_end, tc)
+{
+}
+ATF_TC_BODY(explicit_bzero_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ explicit_bzero(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(explicit_bzero_end);
+ATF_TC_HEAD(explicit_bzero_end, tc)
+{
+}
+ATF_TC_BODY(explicit_bzero_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ explicit_bzero(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(explicit_bzero_heap_before_end);
+ATF_TC_HEAD(explicit_bzero_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(explicit_bzero_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ explicit_bzero(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(explicit_bzero_heap_end);
+ATF_TC_HEAD(explicit_bzero_heap_end, tc)
+{
+}
+ATF_TC_BODY(explicit_bzero_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ explicit_bzero(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(explicit_bzero_heap_after_end);
+ATF_TC_HEAD(explicit_bzero_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(explicit_bzero_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ explicit_bzero(__stack.__buf, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, bcopy_before_end);
+ ATF_TP_ADD_TC(tp, bcopy_end);
+ ATF_TP_ADD_TC(tp, bcopy_heap_before_end);
+ ATF_TP_ADD_TC(tp, bcopy_heap_end);
+ ATF_TP_ADD_TC(tp, bcopy_heap_after_end);
+ ATF_TP_ADD_TC(tp, bzero_before_end);
+ ATF_TP_ADD_TC(tp, bzero_end);
+ ATF_TP_ADD_TC(tp, bzero_heap_before_end);
+ ATF_TP_ADD_TC(tp, bzero_heap_end);
+ ATF_TP_ADD_TC(tp, bzero_heap_after_end);
+ ATF_TP_ADD_TC(tp, explicit_bzero_before_end);
+ ATF_TP_ADD_TC(tp, explicit_bzero_end);
+ ATF_TP_ADD_TC(tp, explicit_bzero_heap_before_end);
+ ATF_TP_ADD_TC(tp, explicit_bzero_heap_end);
+ ATF_TP_ADD_TC(tp, explicit_bzero_heap_after_end);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/secure/fortify_uio_test.c b/lib/libc/tests/secure/fortify_uio_test.c
new file mode 100644
index 000000000000..46b46ed2f7df
--- /dev/null
+++ b/lib/libc/tests/secure/fortify_uio_test.c
@@ -0,0 +1,917 @@
+/* @generated by `generate-fortify-tests.lua "uio"` */
+
+#define _FORTIFY_SOURCE 2
+#define TMPFILE_SIZE (1024 * 32)
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/random.h>
+#include <sys/resource.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <atf-c.h>
+
+static FILE * __unused
+new_fp(size_t __len)
+{
+ static char fpbuf[LINE_MAX];
+ FILE *fp;
+
+ ATF_REQUIRE(__len <= sizeof(fpbuf));
+
+ memset(fpbuf, 'A', sizeof(fpbuf) - 1);
+ fpbuf[sizeof(fpbuf) - 1] = '\0';
+
+ fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
+ ATF_REQUIRE(fp != NULL);
+
+ return (fp);
+}
+
+/*
+ * Create a new symlink to use for readlink(2) style tests, we'll just use a
+ * random target name to have something interesting to look at.
+ */
+static const char * __unused
+new_symlink(size_t __len)
+{
+ static const char linkname[] = "link";
+ char target[MAXNAMLEN];
+ int error;
+
+ ATF_REQUIRE(__len <= sizeof(target));
+
+ arc4random_buf(target, sizeof(target));
+
+ error = unlink(linkname);
+ ATF_REQUIRE(error == 0 || errno == ENOENT);
+
+ error = symlink(target, linkname);
+ ATF_REQUIRE(error == 0);
+
+ return (linkname);
+}
+
+/*
+ * For our purposes, first descriptor will be the reader; we'll send both
+ * raw data and a control message over it so that the result can be used for
+ * any of our recv*() tests.
+ */
+static void __unused
+new_socket(int sock[2])
+{
+ unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 };
+ static char sockbuf[256];
+ ssize_t rv;
+ size_t total = 0;
+ struct msghdr hdr = { 0 };
+ struct cmsghdr *cmsg;
+ int error, fd;
+
+ error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
+ ATF_REQUIRE(error == 0);
+
+ while (total != sizeof(sockbuf)) {
+ rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0);
+
+ ATF_REQUIRE_MSG(rv > 0,
+ "expected bytes sent, got %zd with %zu left (size %zu, total %zu)",
+ rv, sizeof(sockbuf) - total, sizeof(sockbuf), total);
+ ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf),
+ "%zd exceeds total %zu", rv, sizeof(sockbuf));
+ total += rv;
+ }
+
+ hdr.msg_control = ctrl;
+ hdr.msg_controllen = sizeof(ctrl);
+
+ cmsg = CMSG_FIRSTHDR(&hdr);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ fd = STDIN_FILENO;
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
+
+ error = sendmsg(sock[1], &hdr, 0);
+ ATF_REQUIRE(error != -1);
+}
+
+/*
+ * Constructs a tmpfile that we can use for testing read(2) and friends.
+ */
+static int __unused
+new_tmpfile(void)
+{
+ char buf[1024];
+ ssize_t rv;
+ size_t written;
+ int fd;
+
+ fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
+ ATF_REQUIRE(fd >= 0);
+
+ written = 0;
+ while (written < TMPFILE_SIZE) {
+ rv = write(fd, buf, sizeof(buf));
+ ATF_REQUIRE(rv > 0);
+
+ written += rv;
+ }
+
+ ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
+ return (fd);
+}
+
+static void
+disable_coredumps(void)
+{
+ struct rlimit rl = { 0 };
+
+ if (setrlimit(RLIMIT_CORE, &rl) == -1)
+ _exit(EX_OSERR);
+}
+
+/*
+ * Replaces stdin with a file that we can actually read from, for tests where
+ * we want a FILE * or fd that we can get data from.
+ */
+static void __unused
+replace_stdin(void)
+{
+ int fd;
+
+ fd = new_tmpfile();
+
+ (void)dup2(fd, STDIN_FILENO);
+ if (fd != STDIN_FILENO)
+ close(fd);
+}
+
+ATF_TC(readv_before_end);
+ATF_TC_HEAD(readv_before_end, tc)
+{
+}
+ATF_TC_BODY(readv_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct iovec __buf[2];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 2 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ readv(STDIN_FILENO, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(readv_end);
+ATF_TC_HEAD(readv_end, tc)
+{
+}
+ATF_TC_BODY(readv_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct iovec __buf[2];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 2;
+ const size_t __idx __unused = __len - 1;
+
+ readv(STDIN_FILENO, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(readv_after_end);
+ATF_TC_HEAD(readv_after_end, tc)
+{
+}
+ATF_TC_BODY(readv_after_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct iovec __buf[2];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 2 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ readv(STDIN_FILENO, __stack.__buf, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(readv_heap_before_end);
+ATF_TC_HEAD(readv_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(readv_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct iovec * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (2);
+ const size_t __len = 2 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ readv(STDIN_FILENO, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(readv_heap_end);
+ATF_TC_HEAD(readv_heap_end, tc)
+{
+}
+ATF_TC_BODY(readv_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct iovec * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (2);
+ const size_t __len = 2;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ readv(STDIN_FILENO, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(readv_heap_after_end);
+ATF_TC_HEAD(readv_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(readv_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct iovec * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (2);
+ const size_t __len = 2 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ readv(STDIN_FILENO, __stack.__buf, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(readv_iov_before_end);
+ATF_TC_HEAD(readv_iov_before_end, tc)
+{
+}
+ATF_TC_BODY(readv_iov_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ struct iovec iov[1];
+
+ iov[0].iov_base = __stack.__buf;
+ iov[0].iov_len = __len;
+
+ replace_stdin();
+
+ readv(STDIN_FILENO, iov, nitems(iov));
+#undef BUF
+
+}
+
+ATF_TC(readv_iov_end);
+ATF_TC_HEAD(readv_iov_end, tc)
+{
+}
+ATF_TC_BODY(readv_iov_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ struct iovec iov[1];
+
+ iov[0].iov_base = __stack.__buf;
+ iov[0].iov_len = __len;
+
+ replace_stdin();
+
+ readv(STDIN_FILENO, iov, nitems(iov));
+#undef BUF
+
+}
+
+ATF_TC(readv_iov_heap_before_end);
+ATF_TC_HEAD(readv_iov_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(readv_iov_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ struct iovec iov[1];
+
+ __stack.__buf = malloc(__bufsz);
+ iov[0].iov_base = __stack.__buf;
+ iov[0].iov_len = __len;
+
+ replace_stdin();
+
+ readv(STDIN_FILENO, iov, nitems(iov));
+#undef BUF
+
+}
+
+ATF_TC(readv_iov_heap_end);
+ATF_TC_HEAD(readv_iov_heap_end, tc)
+{
+}
+ATF_TC_BODY(readv_iov_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ struct iovec iov[1];
+
+ __stack.__buf = malloc(__bufsz);
+ iov[0].iov_base = __stack.__buf;
+ iov[0].iov_len = __len;
+
+ replace_stdin();
+
+ readv(STDIN_FILENO, iov, nitems(iov));
+#undef BUF
+
+}
+
+ATF_TC(readv_iov_heap_after_end);
+ATF_TC_HEAD(readv_iov_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(readv_iov_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ struct iovec iov[1];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ iov[0].iov_base = __stack.__buf;
+ iov[0].iov_len = __len;
+
+ replace_stdin();
+
+ readv(STDIN_FILENO, iov, nitems(iov));
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(preadv_before_end);
+ATF_TC_HEAD(preadv_before_end, tc)
+{
+}
+ATF_TC_BODY(preadv_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct iovec __buf[2];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 2 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ preadv(STDIN_FILENO, __stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(preadv_end);
+ATF_TC_HEAD(preadv_end, tc)
+{
+}
+ATF_TC_BODY(preadv_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct iovec __buf[2];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 2;
+ const size_t __idx __unused = __len - 1;
+
+ preadv(STDIN_FILENO, __stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(preadv_after_end);
+ATF_TC_HEAD(preadv_after_end, tc)
+{
+}
+ATF_TC_BODY(preadv_after_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct iovec __buf[2];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 2 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ preadv(STDIN_FILENO, __stack.__buf, __len, 0);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(preadv_heap_before_end);
+ATF_TC_HEAD(preadv_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(preadv_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct iovec * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (2);
+ const size_t __len = 2 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ preadv(STDIN_FILENO, __stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(preadv_heap_end);
+ATF_TC_HEAD(preadv_heap_end, tc)
+{
+}
+ATF_TC_BODY(preadv_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct iovec * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (2);
+ const size_t __len = 2;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ preadv(STDIN_FILENO, __stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(preadv_heap_after_end);
+ATF_TC_HEAD(preadv_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(preadv_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ struct iovec * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (2);
+ const size_t __len = 2 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ preadv(STDIN_FILENO, __stack.__buf, __len, 0);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(preadv_iov_before_end);
+ATF_TC_HEAD(preadv_iov_before_end, tc)
+{
+}
+ATF_TC_BODY(preadv_iov_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ struct iovec iov[1];
+
+ iov[0].iov_base = __stack.__buf;
+ iov[0].iov_len = __len;
+
+ replace_stdin();
+
+ preadv(STDIN_FILENO, iov, nitems(iov), 0);
+#undef BUF
+
+}
+
+ATF_TC(preadv_iov_end);
+ATF_TC_HEAD(preadv_iov_end, tc)
+{
+}
+ATF_TC_BODY(preadv_iov_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ struct iovec iov[1];
+
+ iov[0].iov_base = __stack.__buf;
+ iov[0].iov_len = __len;
+
+ replace_stdin();
+
+ preadv(STDIN_FILENO, iov, nitems(iov), 0);
+#undef BUF
+
+}
+
+ATF_TC(preadv_iov_heap_before_end);
+ATF_TC_HEAD(preadv_iov_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(preadv_iov_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ struct iovec iov[1];
+
+ __stack.__buf = malloc(__bufsz);
+ iov[0].iov_base = __stack.__buf;
+ iov[0].iov_len = __len;
+
+ replace_stdin();
+
+ preadv(STDIN_FILENO, iov, nitems(iov), 0);
+#undef BUF
+
+}
+
+ATF_TC(preadv_iov_heap_end);
+ATF_TC_HEAD(preadv_iov_heap_end, tc)
+{
+}
+ATF_TC_BODY(preadv_iov_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ struct iovec iov[1];
+
+ __stack.__buf = malloc(__bufsz);
+ iov[0].iov_base = __stack.__buf;
+ iov[0].iov_len = __len;
+
+ replace_stdin();
+
+ preadv(STDIN_FILENO, iov, nitems(iov), 0);
+#undef BUF
+
+}
+
+ATF_TC(preadv_iov_heap_after_end);
+ATF_TC_HEAD(preadv_iov_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(preadv_iov_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ struct iovec iov[1];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ iov[0].iov_base = __stack.__buf;
+ iov[0].iov_len = __len;
+
+ replace_stdin();
+
+ preadv(STDIN_FILENO, iov, nitems(iov), 0);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, readv_before_end);
+ ATF_TP_ADD_TC(tp, readv_end);
+ ATF_TP_ADD_TC(tp, readv_after_end);
+ ATF_TP_ADD_TC(tp, readv_heap_before_end);
+ ATF_TP_ADD_TC(tp, readv_heap_end);
+ ATF_TP_ADD_TC(tp, readv_heap_after_end);
+ ATF_TP_ADD_TC(tp, readv_iov_before_end);
+ ATF_TP_ADD_TC(tp, readv_iov_end);
+ ATF_TP_ADD_TC(tp, readv_iov_heap_before_end);
+ ATF_TP_ADD_TC(tp, readv_iov_heap_end);
+ ATF_TP_ADD_TC(tp, readv_iov_heap_after_end);
+ ATF_TP_ADD_TC(tp, preadv_before_end);
+ ATF_TP_ADD_TC(tp, preadv_end);
+ ATF_TP_ADD_TC(tp, preadv_after_end);
+ ATF_TP_ADD_TC(tp, preadv_heap_before_end);
+ ATF_TP_ADD_TC(tp, preadv_heap_end);
+ ATF_TP_ADD_TC(tp, preadv_heap_after_end);
+ ATF_TP_ADD_TC(tp, preadv_iov_before_end);
+ ATF_TP_ADD_TC(tp, preadv_iov_end);
+ ATF_TP_ADD_TC(tp, preadv_iov_heap_before_end);
+ ATF_TP_ADD_TC(tp, preadv_iov_heap_end);
+ ATF_TP_ADD_TC(tp, preadv_iov_heap_after_end);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/secure/fortify_unistd_test.c b/lib/libc/tests/secure/fortify_unistd_test.c
new file mode 100644
index 000000000000..b12ef2bbb8ea
--- /dev/null
+++ b/lib/libc/tests/secure/fortify_unistd_test.c
@@ -0,0 +1,2199 @@
+/* @generated by `generate-fortify-tests.lua "unistd"` */
+
+#define _FORTIFY_SOURCE 2
+#define TMPFILE_SIZE (1024 * 32)
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/random.h>
+#include <sys/resource.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <atf-c.h>
+
+static FILE * __unused
+new_fp(size_t __len)
+{
+ static char fpbuf[LINE_MAX];
+ FILE *fp;
+
+ ATF_REQUIRE(__len <= sizeof(fpbuf));
+
+ memset(fpbuf, 'A', sizeof(fpbuf) - 1);
+ fpbuf[sizeof(fpbuf) - 1] = '\0';
+
+ fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
+ ATF_REQUIRE(fp != NULL);
+
+ return (fp);
+}
+
+/*
+ * Create a new symlink to use for readlink(2) style tests, we'll just use a
+ * random target name to have something interesting to look at.
+ */
+static const char * __unused
+new_symlink(size_t __len)
+{
+ static const char linkname[] = "link";
+ char target[MAXNAMLEN];
+ int error;
+
+ ATF_REQUIRE(__len <= sizeof(target));
+
+ arc4random_buf(target, sizeof(target));
+
+ error = unlink(linkname);
+ ATF_REQUIRE(error == 0 || errno == ENOENT);
+
+ error = symlink(target, linkname);
+ ATF_REQUIRE(error == 0);
+
+ return (linkname);
+}
+
+/*
+ * For our purposes, first descriptor will be the reader; we'll send both
+ * raw data and a control message over it so that the result can be used for
+ * any of our recv*() tests.
+ */
+static void __unused
+new_socket(int sock[2])
+{
+ unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 };
+ static char sockbuf[256];
+ ssize_t rv;
+ size_t total = 0;
+ struct msghdr hdr = { 0 };
+ struct cmsghdr *cmsg;
+ int error, fd;
+
+ error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
+ ATF_REQUIRE(error == 0);
+
+ while (total != sizeof(sockbuf)) {
+ rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0);
+
+ ATF_REQUIRE_MSG(rv > 0,
+ "expected bytes sent, got %zd with %zu left (size %zu, total %zu)",
+ rv, sizeof(sockbuf) - total, sizeof(sockbuf), total);
+ ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf),
+ "%zd exceeds total %zu", rv, sizeof(sockbuf));
+ total += rv;
+ }
+
+ hdr.msg_control = ctrl;
+ hdr.msg_controllen = sizeof(ctrl);
+
+ cmsg = CMSG_FIRSTHDR(&hdr);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ fd = STDIN_FILENO;
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
+
+ error = sendmsg(sock[1], &hdr, 0);
+ ATF_REQUIRE(error != -1);
+}
+
+/*
+ * Constructs a tmpfile that we can use for testing read(2) and friends.
+ */
+static int __unused
+new_tmpfile(void)
+{
+ char buf[1024];
+ ssize_t rv;
+ size_t written;
+ int fd;
+
+ fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
+ ATF_REQUIRE(fd >= 0);
+
+ written = 0;
+ while (written < TMPFILE_SIZE) {
+ rv = write(fd, buf, sizeof(buf));
+ ATF_REQUIRE(rv > 0);
+
+ written += rv;
+ }
+
+ ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
+ return (fd);
+}
+
+static void
+disable_coredumps(void)
+{
+ struct rlimit rl = { 0 };
+
+ if (setrlimit(RLIMIT_CORE, &rl) == -1)
+ _exit(EX_OSERR);
+}
+
+/*
+ * Replaces stdin with a file that we can actually read from, for tests where
+ * we want a FILE * or fd that we can get data from.
+ */
+static void __unused
+replace_stdin(void)
+{
+ int fd;
+
+ fd = new_tmpfile();
+
+ (void)dup2(fd, STDIN_FILENO);
+ if (fd != STDIN_FILENO)
+ close(fd);
+}
+
+#define JAIL_HOSTNAME "host.example.com"
+#define JAIL_DOMAINNAME "example.com"
+static void
+dhost_jail(void)
+{
+ struct iovec iov[4];
+ int jid;
+
+ iov[0].iov_base = __DECONST(char *, "host.hostname");
+ iov[0].iov_len = sizeof("host.hostname");
+ iov[1].iov_base = __DECONST(char *, JAIL_HOSTNAME);
+ iov[1].iov_len = sizeof(JAIL_HOSTNAME);
+ iov[2].iov_base = __DECONST(char *, "host.domainname");
+ iov[2].iov_len = sizeof("host.domainname");
+ iov[3].iov_base = __DECONST(char *, JAIL_DOMAINNAME);
+ iov[3].iov_len = sizeof(JAIL_DOMAINNAME);
+
+ jid = jail_set(iov, nitems(iov), JAIL_CREATE | JAIL_ATTACH);
+ ATF_REQUIRE_MSG(jid > 0, "Jail creation failed: %s", strerror(errno));
+}
+
+ATF_TC(getcwd_before_end);
+ATF_TC_HEAD(getcwd_before_end, tc)
+{
+}
+ATF_TC_BODY(getcwd_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[8];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 8 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ getcwd(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getcwd_end);
+ATF_TC_HEAD(getcwd_end, tc)
+{
+}
+ATF_TC_BODY(getcwd_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[8];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 8;
+ const size_t __idx __unused = __len - 1;
+
+ getcwd(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getcwd_heap_before_end);
+ATF_TC_HEAD(getcwd_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(getcwd_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (8);
+ const size_t __len = 8 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ getcwd(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getcwd_heap_end);
+ATF_TC_HEAD(getcwd_heap_end, tc)
+{
+}
+ATF_TC_BODY(getcwd_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (8);
+ const size_t __len = 8;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ getcwd(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getcwd_heap_after_end);
+ATF_TC_HEAD(getcwd_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(getcwd_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (8);
+ const size_t __len = 8 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ getcwd(__stack.__buf, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(getgrouplist_before_end);
+ATF_TC_HEAD(getgrouplist_before_end, tc)
+{
+}
+ATF_TC_BODY(getgrouplist_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ gid_t __buf[4];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 4 - 1;
+ const size_t __idx __unused = __len - 1;
+ int intlen = (int)__len;
+
+ getgrouplist("root", 0, __stack.__buf, &intlen);
+#undef BUF
+
+}
+
+ATF_TC(getgrouplist_end);
+ATF_TC_HEAD(getgrouplist_end, tc)
+{
+}
+ATF_TC_BODY(getgrouplist_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ gid_t __buf[4];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 4;
+ const size_t __idx __unused = __len - 1;
+ int intlen = (int)__len;
+
+ getgrouplist("root", 0, __stack.__buf, &intlen);
+#undef BUF
+
+}
+
+ATF_TC(getgrouplist_heap_before_end);
+ATF_TC_HEAD(getgrouplist_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(getgrouplist_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ gid_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (4);
+ const size_t __len = 4 - 1;
+ const size_t __idx __unused = __len - 1;
+ int intlen = (int)__len;
+
+ __stack.__buf = malloc(__bufsz);
+
+ getgrouplist("root", 0, __stack.__buf, &intlen);
+#undef BUF
+
+}
+
+ATF_TC(getgrouplist_heap_end);
+ATF_TC_HEAD(getgrouplist_heap_end, tc)
+{
+}
+ATF_TC_BODY(getgrouplist_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ gid_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (4);
+ const size_t __len = 4;
+ const size_t __idx __unused = __len - 1;
+ int intlen = (int)__len;
+
+ __stack.__buf = malloc(__bufsz);
+
+ getgrouplist("root", 0, __stack.__buf, &intlen);
+#undef BUF
+
+}
+
+ATF_TC(getgrouplist_heap_after_end);
+ATF_TC_HEAD(getgrouplist_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(getgrouplist_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ gid_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (4);
+ const size_t __len = 4 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ int intlen = (int)__len;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ getgrouplist("root", 0, __stack.__buf, &intlen);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(getgroups_before_end);
+ATF_TC_HEAD(getgroups_before_end, tc)
+{
+}
+ATF_TC_BODY(getgroups_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ gid_t __buf[4];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 4 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ getgroups(__len, __stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(getgroups_end);
+ATF_TC_HEAD(getgroups_end, tc)
+{
+}
+ATF_TC_BODY(getgroups_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ gid_t __buf[4];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 4;
+ const size_t __idx __unused = __len - 1;
+
+ getgroups(__len, __stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(getgroups_heap_before_end);
+ATF_TC_HEAD(getgroups_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(getgroups_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ gid_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (4);
+ const size_t __len = 4 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ getgroups(__len, __stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(getgroups_heap_end);
+ATF_TC_HEAD(getgroups_heap_end, tc)
+{
+}
+ATF_TC_BODY(getgroups_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ gid_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (4);
+ const size_t __len = 4;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ getgroups(__len, __stack.__buf);
+#undef BUF
+
+}
+
+ATF_TC(getgroups_heap_after_end);
+ATF_TC_HEAD(getgroups_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(getgroups_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ gid_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (4);
+ const size_t __len = 4 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ getgroups(__len, __stack.__buf);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(getloginclass_before_end);
+ATF_TC_HEAD(getloginclass_before_end, tc)
+{
+}
+ATF_TC_BODY(getloginclass_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ getloginclass(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getloginclass_end);
+ATF_TC_HEAD(getloginclass_end, tc)
+{
+}
+ATF_TC_BODY(getloginclass_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ getloginclass(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getloginclass_heap_before_end);
+ATF_TC_HEAD(getloginclass_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(getloginclass_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ getloginclass(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getloginclass_heap_end);
+ATF_TC_HEAD(getloginclass_heap_end, tc)
+{
+}
+ATF_TC_BODY(getloginclass_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ getloginclass(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getloginclass_heap_after_end);
+ATF_TC_HEAD(getloginclass_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(getloginclass_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ getloginclass(__stack.__buf, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(pread_before_end);
+ATF_TC_HEAD(pread_before_end, tc)
+{
+}
+ATF_TC_BODY(pread_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[41];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 41 - 1;
+ const size_t __idx __unused = __len - 1;
+ int fd;
+
+ fd = new_tmpfile(); /* Cannot fail */
+
+ pread(fd, __stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(pread_end);
+ATF_TC_HEAD(pread_end, tc)
+{
+}
+ATF_TC_BODY(pread_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[41];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 41;
+ const size_t __idx __unused = __len - 1;
+ int fd;
+
+ fd = new_tmpfile(); /* Cannot fail */
+
+ pread(fd, __stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(pread_heap_before_end);
+ATF_TC_HEAD(pread_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(pread_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (41);
+ const size_t __len = 41 - 1;
+ const size_t __idx __unused = __len - 1;
+ int fd;
+
+ __stack.__buf = malloc(__bufsz);
+ fd = new_tmpfile(); /* Cannot fail */
+
+ pread(fd, __stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(pread_heap_end);
+ATF_TC_HEAD(pread_heap_end, tc)
+{
+}
+ATF_TC_BODY(pread_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (41);
+ const size_t __len = 41;
+ const size_t __idx __unused = __len - 1;
+ int fd;
+
+ __stack.__buf = malloc(__bufsz);
+ fd = new_tmpfile(); /* Cannot fail */
+
+ pread(fd, __stack.__buf, __len, 0);
+#undef BUF
+
+}
+
+ATF_TC(pread_heap_after_end);
+ATF_TC_HEAD(pread_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(pread_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (41);
+ const size_t __len = 41 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ int fd;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ fd = new_tmpfile(); /* Cannot fail */
+
+ pread(fd, __stack.__buf, __len, 0);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(read_before_end);
+ATF_TC_HEAD(read_before_end, tc)
+{
+}
+ATF_TC_BODY(read_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[41];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 41 - 1;
+ const size_t __idx __unused = __len - 1;
+ int fd;
+
+ fd = new_tmpfile(); /* Cannot fail */
+
+ read(fd, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(read_end);
+ATF_TC_HEAD(read_end, tc)
+{
+}
+ATF_TC_BODY(read_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[41];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 41;
+ const size_t __idx __unused = __len - 1;
+ int fd;
+
+ fd = new_tmpfile(); /* Cannot fail */
+
+ read(fd, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(read_heap_before_end);
+ATF_TC_HEAD(read_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(read_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (41);
+ const size_t __len = 41 - 1;
+ const size_t __idx __unused = __len - 1;
+ int fd;
+
+ __stack.__buf = malloc(__bufsz);
+ fd = new_tmpfile(); /* Cannot fail */
+
+ read(fd, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(read_heap_end);
+ATF_TC_HEAD(read_heap_end, tc)
+{
+}
+ATF_TC_BODY(read_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (41);
+ const size_t __len = 41;
+ const size_t __idx __unused = __len - 1;
+ int fd;
+
+ __stack.__buf = malloc(__bufsz);
+ fd = new_tmpfile(); /* Cannot fail */
+
+ read(fd, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(read_heap_after_end);
+ATF_TC_HEAD(read_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(read_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (41);
+ const size_t __len = 41 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ int fd;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ fd = new_tmpfile(); /* Cannot fail */
+
+ read(fd, __stack.__buf, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(readlink_before_end);
+ATF_TC_HEAD(readlink_before_end, tc)
+{
+}
+ATF_TC_BODY(readlink_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ const char *path;
+
+ path = new_symlink(__len); /* Cannot fail */
+
+ readlink(path, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(readlink_end);
+ATF_TC_HEAD(readlink_end, tc)
+{
+}
+ATF_TC_BODY(readlink_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ const char *path;
+
+ path = new_symlink(__len); /* Cannot fail */
+
+ readlink(path, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(readlink_heap_before_end);
+ATF_TC_HEAD(readlink_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(readlink_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ const char *path;
+
+ __stack.__buf = malloc(__bufsz);
+ path = new_symlink(__len); /* Cannot fail */
+
+ readlink(path, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(readlink_heap_end);
+ATF_TC_HEAD(readlink_heap_end, tc)
+{
+}
+ATF_TC_BODY(readlink_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ const char *path;
+
+ __stack.__buf = malloc(__bufsz);
+ path = new_symlink(__len); /* Cannot fail */
+
+ readlink(path, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(readlink_heap_after_end);
+ATF_TC_HEAD(readlink_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(readlink_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ const char *path;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ path = new_symlink(__len); /* Cannot fail */
+
+ readlink(path, __stack.__buf, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(readlinkat_before_end);
+ATF_TC_HEAD(readlinkat_before_end, tc)
+{
+}
+ATF_TC_BODY(readlinkat_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ const char *path;
+
+ path = new_symlink(__len); /* Cannot fail */
+
+ readlinkat(AT_FDCWD, path, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(readlinkat_end);
+ATF_TC_HEAD(readlinkat_end, tc)
+{
+}
+ATF_TC_BODY(readlinkat_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ const char *path;
+
+ path = new_symlink(__len); /* Cannot fail */
+
+ readlinkat(AT_FDCWD, path, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(readlinkat_heap_before_end);
+ATF_TC_HEAD(readlinkat_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(readlinkat_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ const char *path;
+
+ __stack.__buf = malloc(__bufsz);
+ path = new_symlink(__len); /* Cannot fail */
+
+ readlinkat(AT_FDCWD, path, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(readlinkat_heap_end);
+ATF_TC_HEAD(readlinkat_heap_end, tc)
+{
+}
+ATF_TC_BODY(readlinkat_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ const char *path;
+
+ __stack.__buf = malloc(__bufsz);
+ path = new_symlink(__len); /* Cannot fail */
+
+ readlinkat(AT_FDCWD, path, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(readlinkat_heap_after_end);
+ATF_TC_HEAD(readlinkat_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(readlinkat_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ const char *path;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ path = new_symlink(__len); /* Cannot fail */
+
+ readlinkat(AT_FDCWD, path, __stack.__buf, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(getdomainname_before_end);
+ATF_TC_HEAD(getdomainname_before_end, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(getdomainname_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[12];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 12 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ dhost_jail();
+ getdomainname(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getdomainname_end);
+ATF_TC_HEAD(getdomainname_end, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(getdomainname_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[12];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 12;
+ const size_t __idx __unused = __len - 1;
+
+ dhost_jail();
+ getdomainname(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getdomainname_heap_before_end);
+ATF_TC_HEAD(getdomainname_heap_before_end, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(getdomainname_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (12);
+ const size_t __len = 12 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ dhost_jail();
+ __stack.__buf = malloc(__bufsz);
+
+ getdomainname(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getdomainname_heap_end);
+ATF_TC_HEAD(getdomainname_heap_end, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(getdomainname_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (12);
+ const size_t __len = 12;
+ const size_t __idx __unused = __len - 1;
+
+ dhost_jail();
+ __stack.__buf = malloc(__bufsz);
+
+ getdomainname(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getdomainname_heap_after_end);
+ATF_TC_HEAD(getdomainname_heap_after_end, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(getdomainname_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (12);
+ const size_t __len = 12 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ dhost_jail();
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ getdomainname(__stack.__buf, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(getentropy_before_end);
+ATF_TC_HEAD(getentropy_before_end, tc)
+{
+}
+ATF_TC_BODY(getentropy_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ getentropy(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getentropy_end);
+ATF_TC_HEAD(getentropy_end, tc)
+{
+}
+ATF_TC_BODY(getentropy_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ getentropy(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getentropy_heap_before_end);
+ATF_TC_HEAD(getentropy_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(getentropy_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ getentropy(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getentropy_heap_end);
+ATF_TC_HEAD(getentropy_heap_end, tc)
+{
+}
+ATF_TC_BODY(getentropy_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ getentropy(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getentropy_heap_after_end);
+ATF_TC_HEAD(getentropy_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(getentropy_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ getentropy(__stack.__buf, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(gethostname_before_end);
+ATF_TC_HEAD(gethostname_before_end, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(gethostname_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[17];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 17 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ dhost_jail();
+ gethostname(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(gethostname_end);
+ATF_TC_HEAD(gethostname_end, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(gethostname_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[17];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 17;
+ const size_t __idx __unused = __len - 1;
+
+ dhost_jail();
+ gethostname(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(gethostname_heap_before_end);
+ATF_TC_HEAD(gethostname_heap_before_end, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(gethostname_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (17);
+ const size_t __len = 17 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ dhost_jail();
+ __stack.__buf = malloc(__bufsz);
+
+ gethostname(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(gethostname_heap_end);
+ATF_TC_HEAD(gethostname_heap_end, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(gethostname_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (17);
+ const size_t __len = 17;
+ const size_t __idx __unused = __len - 1;
+
+ dhost_jail();
+ __stack.__buf = malloc(__bufsz);
+
+ gethostname(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(gethostname_heap_after_end);
+ATF_TC_HEAD(gethostname_heap_after_end, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(gethostname_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (17);
+ const size_t __len = 17 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ dhost_jail();
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ gethostname(__stack.__buf, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(getlogin_r_before_end);
+ATF_TC_HEAD(getlogin_r_before_end, tc)
+{
+}
+ATF_TC_BODY(getlogin_r_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[MAXLOGNAME + 1];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = MAXLOGNAME + 1 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ getlogin_r(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getlogin_r_end);
+ATF_TC_HEAD(getlogin_r_end, tc)
+{
+}
+ATF_TC_BODY(getlogin_r_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[MAXLOGNAME + 1];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = MAXLOGNAME + 1;
+ const size_t __idx __unused = __len - 1;
+
+ getlogin_r(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getlogin_r_heap_before_end);
+ATF_TC_HEAD(getlogin_r_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(getlogin_r_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (MAXLOGNAME + 1);
+ const size_t __len = MAXLOGNAME + 1 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ getlogin_r(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getlogin_r_heap_end);
+ATF_TC_HEAD(getlogin_r_heap_end, tc)
+{
+}
+ATF_TC_BODY(getlogin_r_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (MAXLOGNAME + 1);
+ const size_t __len = MAXLOGNAME + 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ getlogin_r(__stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(getlogin_r_heap_after_end);
+ATF_TC_HEAD(getlogin_r_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(getlogin_r_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (MAXLOGNAME + 1);
+ const size_t __len = MAXLOGNAME + 1 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ getlogin_r(__stack.__buf, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(ttyname_r_before_end);
+ATF_TC_HEAD(ttyname_r_before_end, tc)
+{
+}
+ATF_TC_BODY(ttyname_r_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ int fd;
+
+ fd = STDIN_FILENO;
+ if (!isatty(fd))
+ atf_tc_skip("stdin is not an fd");
+
+ ttyname_r(fd, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(ttyname_r_end);
+ATF_TC_HEAD(ttyname_r_end, tc)
+{
+}
+ATF_TC_BODY(ttyname_r_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ int fd;
+
+ fd = STDIN_FILENO;
+ if (!isatty(fd))
+ atf_tc_skip("stdin is not an fd");
+
+ ttyname_r(fd, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(ttyname_r_heap_before_end);
+ATF_TC_HEAD(ttyname_r_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(ttyname_r_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ int fd;
+
+ fd = STDIN_FILENO;
+ if (!isatty(fd))
+ atf_tc_skip("stdin is not an fd");
+
+ __stack.__buf = malloc(__bufsz);
+
+ ttyname_r(fd, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(ttyname_r_heap_end);
+ATF_TC_HEAD(ttyname_r_heap_end, tc)
+{
+}
+ATF_TC_BODY(ttyname_r_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ int fd;
+
+ fd = STDIN_FILENO;
+ if (!isatty(fd))
+ atf_tc_skip("stdin is not an fd");
+
+ __stack.__buf = malloc(__bufsz);
+
+ ttyname_r(fd, __stack.__buf, __len);
+#undef BUF
+
+}
+
+ATF_TC(ttyname_r_heap_after_end);
+ATF_TC_HEAD(ttyname_r_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(ttyname_r_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ unsigned char * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ int fd;
+
+ fd = STDIN_FILENO;
+ if (!isatty(fd))
+ atf_tc_skip("stdin is not an fd");
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ ttyname_r(fd, __stack.__buf, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, getcwd_before_end);
+ ATF_TP_ADD_TC(tp, getcwd_end);
+ ATF_TP_ADD_TC(tp, getcwd_heap_before_end);
+ ATF_TP_ADD_TC(tp, getcwd_heap_end);
+ ATF_TP_ADD_TC(tp, getcwd_heap_after_end);
+ ATF_TP_ADD_TC(tp, getgrouplist_before_end);
+ ATF_TP_ADD_TC(tp, getgrouplist_end);
+ ATF_TP_ADD_TC(tp, getgrouplist_heap_before_end);
+ ATF_TP_ADD_TC(tp, getgrouplist_heap_end);
+ ATF_TP_ADD_TC(tp, getgrouplist_heap_after_end);
+ ATF_TP_ADD_TC(tp, getgroups_before_end);
+ ATF_TP_ADD_TC(tp, getgroups_end);
+ ATF_TP_ADD_TC(tp, getgroups_heap_before_end);
+ ATF_TP_ADD_TC(tp, getgroups_heap_end);
+ ATF_TP_ADD_TC(tp, getgroups_heap_after_end);
+ ATF_TP_ADD_TC(tp, getloginclass_before_end);
+ ATF_TP_ADD_TC(tp, getloginclass_end);
+ ATF_TP_ADD_TC(tp, getloginclass_heap_before_end);
+ ATF_TP_ADD_TC(tp, getloginclass_heap_end);
+ ATF_TP_ADD_TC(tp, getloginclass_heap_after_end);
+ ATF_TP_ADD_TC(tp, pread_before_end);
+ ATF_TP_ADD_TC(tp, pread_end);
+ ATF_TP_ADD_TC(tp, pread_heap_before_end);
+ ATF_TP_ADD_TC(tp, pread_heap_end);
+ ATF_TP_ADD_TC(tp, pread_heap_after_end);
+ ATF_TP_ADD_TC(tp, read_before_end);
+ ATF_TP_ADD_TC(tp, read_end);
+ ATF_TP_ADD_TC(tp, read_heap_before_end);
+ ATF_TP_ADD_TC(tp, read_heap_end);
+ ATF_TP_ADD_TC(tp, read_heap_after_end);
+ ATF_TP_ADD_TC(tp, readlink_before_end);
+ ATF_TP_ADD_TC(tp, readlink_end);
+ ATF_TP_ADD_TC(tp, readlink_heap_before_end);
+ ATF_TP_ADD_TC(tp, readlink_heap_end);
+ ATF_TP_ADD_TC(tp, readlink_heap_after_end);
+ ATF_TP_ADD_TC(tp, readlinkat_before_end);
+ ATF_TP_ADD_TC(tp, readlinkat_end);
+ ATF_TP_ADD_TC(tp, readlinkat_heap_before_end);
+ ATF_TP_ADD_TC(tp, readlinkat_heap_end);
+ ATF_TP_ADD_TC(tp, readlinkat_heap_after_end);
+ ATF_TP_ADD_TC(tp, getdomainname_before_end);
+ ATF_TP_ADD_TC(tp, getdomainname_end);
+ ATF_TP_ADD_TC(tp, getdomainname_heap_before_end);
+ ATF_TP_ADD_TC(tp, getdomainname_heap_end);
+ ATF_TP_ADD_TC(tp, getdomainname_heap_after_end);
+ ATF_TP_ADD_TC(tp, getentropy_before_end);
+ ATF_TP_ADD_TC(tp, getentropy_end);
+ ATF_TP_ADD_TC(tp, getentropy_heap_before_end);
+ ATF_TP_ADD_TC(tp, getentropy_heap_end);
+ ATF_TP_ADD_TC(tp, getentropy_heap_after_end);
+ ATF_TP_ADD_TC(tp, gethostname_before_end);
+ ATF_TP_ADD_TC(tp, gethostname_end);
+ ATF_TP_ADD_TC(tp, gethostname_heap_before_end);
+ ATF_TP_ADD_TC(tp, gethostname_heap_end);
+ ATF_TP_ADD_TC(tp, gethostname_heap_after_end);
+ ATF_TP_ADD_TC(tp, getlogin_r_before_end);
+ ATF_TP_ADD_TC(tp, getlogin_r_end);
+ ATF_TP_ADD_TC(tp, getlogin_r_heap_before_end);
+ ATF_TP_ADD_TC(tp, getlogin_r_heap_end);
+ ATF_TP_ADD_TC(tp, getlogin_r_heap_after_end);
+ ATF_TP_ADD_TC(tp, ttyname_r_before_end);
+ ATF_TP_ADD_TC(tp, ttyname_r_end);
+ ATF_TP_ADD_TC(tp, ttyname_r_heap_before_end);
+ ATF_TP_ADD_TC(tp, ttyname_r_heap_end);
+ ATF_TP_ADD_TC(tp, ttyname_r_heap_after_end);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/secure/fortify_wchar_test.c b/lib/libc/tests/secure/fortify_wchar_test.c
new file mode 100644
index 000000000000..43c7997bc6a3
--- /dev/null
+++ b/lib/libc/tests/secure/fortify_wchar_test.c
@@ -0,0 +1,2124 @@
+/* @generated by `generate-fortify-tests.lua "wchar"` */
+
+#define _FORTIFY_SOURCE 2
+#define TMPFILE_SIZE (1024 * 32)
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/random.h>
+#include <sys/resource.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <wchar.h>
+#include <atf-c.h>
+
+static FILE * __unused
+new_fp(size_t __len)
+{
+ static char fpbuf[LINE_MAX];
+ FILE *fp;
+
+ ATF_REQUIRE(__len <= sizeof(fpbuf));
+
+ memset(fpbuf, 'A', sizeof(fpbuf) - 1);
+ fpbuf[sizeof(fpbuf) - 1] = '\0';
+
+ fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
+ ATF_REQUIRE(fp != NULL);
+
+ return (fp);
+}
+
+/*
+ * Create a new symlink to use for readlink(2) style tests, we'll just use a
+ * random target name to have something interesting to look at.
+ */
+static const char * __unused
+new_symlink(size_t __len)
+{
+ static const char linkname[] = "link";
+ char target[MAXNAMLEN];
+ int error;
+
+ ATF_REQUIRE(__len <= sizeof(target));
+
+ arc4random_buf(target, sizeof(target));
+
+ error = unlink(linkname);
+ ATF_REQUIRE(error == 0 || errno == ENOENT);
+
+ error = symlink(target, linkname);
+ ATF_REQUIRE(error == 0);
+
+ return (linkname);
+}
+
+/*
+ * For our purposes, first descriptor will be the reader; we'll send both
+ * raw data and a control message over it so that the result can be used for
+ * any of our recv*() tests.
+ */
+static void __unused
+new_socket(int sock[2])
+{
+ unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 };
+ static char sockbuf[256];
+ ssize_t rv;
+ size_t total = 0;
+ struct msghdr hdr = { 0 };
+ struct cmsghdr *cmsg;
+ int error, fd;
+
+ error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
+ ATF_REQUIRE(error == 0);
+
+ while (total != sizeof(sockbuf)) {
+ rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0);
+
+ ATF_REQUIRE_MSG(rv > 0,
+ "expected bytes sent, got %zd with %zu left (size %zu, total %zu)",
+ rv, sizeof(sockbuf) - total, sizeof(sockbuf), total);
+ ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf),
+ "%zd exceeds total %zu", rv, sizeof(sockbuf));
+ total += rv;
+ }
+
+ hdr.msg_control = ctrl;
+ hdr.msg_controllen = sizeof(ctrl);
+
+ cmsg = CMSG_FIRSTHDR(&hdr);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ fd = STDIN_FILENO;
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
+
+ error = sendmsg(sock[1], &hdr, 0);
+ ATF_REQUIRE(error != -1);
+}
+
+/*
+ * Constructs a tmpfile that we can use for testing read(2) and friends.
+ */
+static int __unused
+new_tmpfile(void)
+{
+ char buf[1024];
+ ssize_t rv;
+ size_t written;
+ int fd;
+
+ fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
+ ATF_REQUIRE(fd >= 0);
+
+ written = 0;
+ while (written < TMPFILE_SIZE) {
+ rv = write(fd, buf, sizeof(buf));
+ ATF_REQUIRE(rv > 0);
+
+ written += rv;
+ }
+
+ ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
+ return (fd);
+}
+
+static void
+disable_coredumps(void)
+{
+ struct rlimit rl = { 0 };
+
+ if (setrlimit(RLIMIT_CORE, &rl) == -1)
+ _exit(EX_OSERR);
+}
+
+/*
+ * Replaces stdin with a file that we can actually read from, for tests where
+ * we want a FILE * or fd that we can get data from.
+ */
+static void __unused
+replace_stdin(void)
+{
+ int fd;
+
+ fd = new_tmpfile();
+
+ (void)dup2(fd, STDIN_FILENO);
+ if (fd != STDIN_FILENO)
+ close(fd);
+}
+
+ATF_TC(wmemcpy_before_end);
+ATF_TC_HEAD(wmemcpy_before_end, tc)
+{
+}
+ATF_TC_BODY(wmemcpy_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len + 10];
+
+ wmemcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wmemcpy_end);
+ATF_TC_HEAD(wmemcpy_end, tc)
+{
+}
+ATF_TC_BODY(wmemcpy_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len + 10];
+
+ wmemcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wmemcpy_heap_before_end);
+ATF_TC_HEAD(wmemcpy_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(wmemcpy_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ wmemcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wmemcpy_heap_end);
+ATF_TC_HEAD(wmemcpy_heap_end, tc)
+{
+}
+ATF_TC_BODY(wmemcpy_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ wmemcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wmemcpy_heap_after_end);
+ATF_TC_HEAD(wmemcpy_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(wmemcpy_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ wchar_t src[__len + 10];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ wmemcpy(__stack.__buf, src, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(wmempcpy_before_end);
+ATF_TC_HEAD(wmempcpy_before_end, tc)
+{
+}
+ATF_TC_BODY(wmempcpy_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len + 10];
+
+ wmempcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wmempcpy_end);
+ATF_TC_HEAD(wmempcpy_end, tc)
+{
+}
+ATF_TC_BODY(wmempcpy_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len + 10];
+
+ wmempcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wmempcpy_heap_before_end);
+ATF_TC_HEAD(wmempcpy_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(wmempcpy_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ wmempcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wmempcpy_heap_end);
+ATF_TC_HEAD(wmempcpy_heap_end, tc)
+{
+}
+ATF_TC_BODY(wmempcpy_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ wmempcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wmempcpy_heap_after_end);
+ATF_TC_HEAD(wmempcpy_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(wmempcpy_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ wchar_t src[__len + 10];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ wmempcpy(__stack.__buf, src, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(wmemmove_before_end);
+ATF_TC_HEAD(wmemmove_before_end, tc)
+{
+}
+ATF_TC_BODY(wmemmove_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len + 10];
+
+ wmemmove(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wmemmove_end);
+ATF_TC_HEAD(wmemmove_end, tc)
+{
+}
+ATF_TC_BODY(wmemmove_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len + 10];
+
+ wmemmove(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wmemmove_heap_before_end);
+ATF_TC_HEAD(wmemmove_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(wmemmove_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ wmemmove(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wmemmove_heap_end);
+ATF_TC_HEAD(wmemmove_heap_end, tc)
+{
+}
+ATF_TC_BODY(wmemmove_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len + 10];
+
+ __stack.__buf = malloc(__bufsz);
+
+ wmemmove(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wmemmove_heap_after_end);
+ATF_TC_HEAD(wmemmove_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(wmemmove_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ wchar_t src[__len + 10];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ wmemmove(__stack.__buf, src, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(wmemset_before_end);
+ATF_TC_HEAD(wmemset_before_end, tc)
+{
+}
+ATF_TC_BODY(wmemset_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ wmemset(__stack.__buf, L'0', __len);
+#undef BUF
+
+}
+
+ATF_TC(wmemset_end);
+ATF_TC_HEAD(wmemset_end, tc)
+{
+}
+ATF_TC_BODY(wmemset_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ wmemset(__stack.__buf, L'0', __len);
+#undef BUF
+
+}
+
+ATF_TC(wmemset_heap_before_end);
+ATF_TC_HEAD(wmemset_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(wmemset_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ wmemset(__stack.__buf, L'0', __len);
+#undef BUF
+
+}
+
+ATF_TC(wmemset_heap_end);
+ATF_TC_HEAD(wmemset_heap_end, tc)
+{
+}
+ATF_TC_BODY(wmemset_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+
+ __stack.__buf = malloc(__bufsz);
+
+ wmemset(__stack.__buf, L'0', __len);
+#undef BUF
+
+}
+
+ATF_TC(wmemset_heap_after_end);
+ATF_TC_HEAD(wmemset_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(wmemset_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+
+ wmemset(__stack.__buf, L'0', __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(wcpcpy_before_end);
+ATF_TC_HEAD(wcpcpy_before_end, tc)
+{
+}
+ATF_TC_BODY(wcpcpy_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcpcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(wcpcpy_end);
+ATF_TC_HEAD(wcpcpy_end, tc)
+{
+}
+ATF_TC_BODY(wcpcpy_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcpcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(wcpcpy_heap_before_end);
+ATF_TC_HEAD(wcpcpy_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(wcpcpy_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcpcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(wcpcpy_heap_end);
+ATF_TC_HEAD(wcpcpy_heap_end, tc)
+{
+}
+ATF_TC_BODY(wcpcpy_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcpcpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(wcpcpy_heap_after_end);
+ATF_TC_HEAD(wcpcpy_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(wcpcpy_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ wchar_t src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcpcpy(__stack.__buf, src);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(wcpncpy_before_end);
+ATF_TC_HEAD(wcpncpy_before_end, tc)
+{
+}
+ATF_TC_BODY(wcpncpy_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcpncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcpncpy_end);
+ATF_TC_HEAD(wcpncpy_end, tc)
+{
+}
+ATF_TC_BODY(wcpncpy_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcpncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcpncpy_heap_before_end);
+ATF_TC_HEAD(wcpncpy_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(wcpncpy_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcpncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcpncpy_heap_end);
+ATF_TC_HEAD(wcpncpy_heap_end, tc)
+{
+}
+ATF_TC_BODY(wcpncpy_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcpncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcpncpy_heap_after_end);
+ATF_TC_HEAD(wcpncpy_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(wcpncpy_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ wchar_t src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcpncpy(__stack.__buf, src, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(wcscat_before_end);
+ATF_TC_HEAD(wcscat_before_end, tc)
+{
+}
+ATF_TC_BODY(wcscat_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcscat(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(wcscat_end);
+ATF_TC_HEAD(wcscat_end, tc)
+{
+}
+ATF_TC_BODY(wcscat_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcscat(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(wcscat_heap_before_end);
+ATF_TC_HEAD(wcscat_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(wcscat_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcscat(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(wcscat_heap_end);
+ATF_TC_HEAD(wcscat_heap_end, tc)
+{
+}
+ATF_TC_BODY(wcscat_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcscat(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(wcscat_heap_after_end);
+ATF_TC_HEAD(wcscat_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(wcscat_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ wchar_t src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcscat(__stack.__buf, src);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(wcslcat_before_end);
+ATF_TC_HEAD(wcslcat_before_end, tc)
+{
+}
+ATF_TC_BODY(wcslcat_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcslcat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcslcat_end);
+ATF_TC_HEAD(wcslcat_end, tc)
+{
+}
+ATF_TC_BODY(wcslcat_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcslcat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcslcat_heap_before_end);
+ATF_TC_HEAD(wcslcat_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(wcslcat_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcslcat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcslcat_heap_end);
+ATF_TC_HEAD(wcslcat_heap_end, tc)
+{
+}
+ATF_TC_BODY(wcslcat_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcslcat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcslcat_heap_after_end);
+ATF_TC_HEAD(wcslcat_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(wcslcat_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ wchar_t src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcslcat(__stack.__buf, src, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(wcsncat_before_end);
+ATF_TC_HEAD(wcsncat_before_end, tc)
+{
+}
+ATF_TC_BODY(wcsncat_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcsncat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcsncat_end);
+ATF_TC_HEAD(wcsncat_end, tc)
+{
+}
+ATF_TC_BODY(wcsncat_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcsncat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcsncat_heap_before_end);
+ATF_TC_HEAD(wcsncat_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(wcsncat_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcsncat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcsncat_heap_end);
+ATF_TC_HEAD(wcsncat_heap_end, tc)
+{
+}
+ATF_TC_BODY(wcsncat_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcsncat(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcsncat_heap_after_end);
+ATF_TC_HEAD(wcsncat_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(wcsncat_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ wchar_t src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcsncat(__stack.__buf, src, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(wcscpy_before_end);
+ATF_TC_HEAD(wcscpy_before_end, tc)
+{
+}
+ATF_TC_BODY(wcscpy_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcscpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(wcscpy_end);
+ATF_TC_HEAD(wcscpy_end, tc)
+{
+}
+ATF_TC_BODY(wcscpy_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcscpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(wcscpy_heap_before_end);
+ATF_TC_HEAD(wcscpy_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(wcscpy_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcscpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(wcscpy_heap_end);
+ATF_TC_HEAD(wcscpy_heap_end, tc)
+{
+}
+ATF_TC_BODY(wcscpy_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcscpy(__stack.__buf, src);
+#undef BUF
+
+}
+
+ATF_TC(wcscpy_heap_after_end);
+ATF_TC_HEAD(wcscpy_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(wcscpy_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ wchar_t src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcscpy(__stack.__buf, src);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(wcslcpy_before_end);
+ATF_TC_HEAD(wcslcpy_before_end, tc)
+{
+}
+ATF_TC_BODY(wcslcpy_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcslcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcslcpy_end);
+ATF_TC_HEAD(wcslcpy_end, tc)
+{
+}
+ATF_TC_BODY(wcslcpy_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcslcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcslcpy_heap_before_end);
+ATF_TC_HEAD(wcslcpy_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(wcslcpy_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcslcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcslcpy_heap_end);
+ATF_TC_HEAD(wcslcpy_heap_end, tc)
+{
+}
+ATF_TC_BODY(wcslcpy_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcslcpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcslcpy_heap_after_end);
+ATF_TC_HEAD(wcslcpy_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(wcslcpy_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ wchar_t src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcslcpy(__stack.__buf, src, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TC(wcsncpy_before_end);
+ATF_TC_HEAD(wcsncpy_before_end, tc)
+{
+}
+ATF_TC_BODY(wcsncpy_before_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcsncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcsncpy_end);
+ATF_TC_HEAD(wcsncpy_end, tc)
+{
+}
+ATF_TC_BODY(wcsncpy_end, tc)
+{
+#define BUF &__stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t __buf[42];
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(__stack.__buf);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcsncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcsncpy_heap_before_end);
+ATF_TC_HEAD(wcsncpy_heap_before_end, tc)
+{
+}
+ATF_TC_BODY(wcsncpy_heap_before_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 - 1;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcsncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcsncpy_heap_end);
+ATF_TC_HEAD(wcsncpy_heap_end, tc)
+{
+}
+ATF_TC_BODY(wcsncpy_heap_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42;
+ const size_t __idx __unused = __len - 1;
+ wchar_t src[__len];
+
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcsncpy(__stack.__buf, src, __len);
+#undef BUF
+
+}
+
+ATF_TC(wcsncpy_heap_after_end);
+ATF_TC_HEAD(wcsncpy_heap_after_end, tc)
+{
+}
+ATF_TC_BODY(wcsncpy_heap_after_end, tc)
+{
+#define BUF __stack.__buf
+ struct {
+ uint8_t padding_l;
+ wchar_t * __buf;
+ uint8_t padding_r;
+ } __stack;
+ const size_t __bufsz __unused = sizeof(*__stack.__buf) * (42);
+ const size_t __len = 42 + 1;
+ const size_t __idx __unused = __len - 1;
+ pid_t __child;
+ int __status;
+ wchar_t src[__len];
+
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+ __stack.__buf = malloc(__bufsz);
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+
+ wcsncpy(__stack.__buf, src, __len);
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+#undef BUF
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, wmemcpy_before_end);
+ ATF_TP_ADD_TC(tp, wmemcpy_end);
+ ATF_TP_ADD_TC(tp, wmemcpy_heap_before_end);
+ ATF_TP_ADD_TC(tp, wmemcpy_heap_end);
+ ATF_TP_ADD_TC(tp, wmemcpy_heap_after_end);
+ ATF_TP_ADD_TC(tp, wmempcpy_before_end);
+ ATF_TP_ADD_TC(tp, wmempcpy_end);
+ ATF_TP_ADD_TC(tp, wmempcpy_heap_before_end);
+ ATF_TP_ADD_TC(tp, wmempcpy_heap_end);
+ ATF_TP_ADD_TC(tp, wmempcpy_heap_after_end);
+ ATF_TP_ADD_TC(tp, wmemmove_before_end);
+ ATF_TP_ADD_TC(tp, wmemmove_end);
+ ATF_TP_ADD_TC(tp, wmemmove_heap_before_end);
+ ATF_TP_ADD_TC(tp, wmemmove_heap_end);
+ ATF_TP_ADD_TC(tp, wmemmove_heap_after_end);
+ ATF_TP_ADD_TC(tp, wmemset_before_end);
+ ATF_TP_ADD_TC(tp, wmemset_end);
+ ATF_TP_ADD_TC(tp, wmemset_heap_before_end);
+ ATF_TP_ADD_TC(tp, wmemset_heap_end);
+ ATF_TP_ADD_TC(tp, wmemset_heap_after_end);
+ ATF_TP_ADD_TC(tp, wcpcpy_before_end);
+ ATF_TP_ADD_TC(tp, wcpcpy_end);
+ ATF_TP_ADD_TC(tp, wcpcpy_heap_before_end);
+ ATF_TP_ADD_TC(tp, wcpcpy_heap_end);
+ ATF_TP_ADD_TC(tp, wcpcpy_heap_after_end);
+ ATF_TP_ADD_TC(tp, wcpncpy_before_end);
+ ATF_TP_ADD_TC(tp, wcpncpy_end);
+ ATF_TP_ADD_TC(tp, wcpncpy_heap_before_end);
+ ATF_TP_ADD_TC(tp, wcpncpy_heap_end);
+ ATF_TP_ADD_TC(tp, wcpncpy_heap_after_end);
+ ATF_TP_ADD_TC(tp, wcscat_before_end);
+ ATF_TP_ADD_TC(tp, wcscat_end);
+ ATF_TP_ADD_TC(tp, wcscat_heap_before_end);
+ ATF_TP_ADD_TC(tp, wcscat_heap_end);
+ ATF_TP_ADD_TC(tp, wcscat_heap_after_end);
+ ATF_TP_ADD_TC(tp, wcslcat_before_end);
+ ATF_TP_ADD_TC(tp, wcslcat_end);
+ ATF_TP_ADD_TC(tp, wcslcat_heap_before_end);
+ ATF_TP_ADD_TC(tp, wcslcat_heap_end);
+ ATF_TP_ADD_TC(tp, wcslcat_heap_after_end);
+ ATF_TP_ADD_TC(tp, wcsncat_before_end);
+ ATF_TP_ADD_TC(tp, wcsncat_end);
+ ATF_TP_ADD_TC(tp, wcsncat_heap_before_end);
+ ATF_TP_ADD_TC(tp, wcsncat_heap_end);
+ ATF_TP_ADD_TC(tp, wcsncat_heap_after_end);
+ ATF_TP_ADD_TC(tp, wcscpy_before_end);
+ ATF_TP_ADD_TC(tp, wcscpy_end);
+ ATF_TP_ADD_TC(tp, wcscpy_heap_before_end);
+ ATF_TP_ADD_TC(tp, wcscpy_heap_end);
+ ATF_TP_ADD_TC(tp, wcscpy_heap_after_end);
+ ATF_TP_ADD_TC(tp, wcslcpy_before_end);
+ ATF_TP_ADD_TC(tp, wcslcpy_end);
+ ATF_TP_ADD_TC(tp, wcslcpy_heap_before_end);
+ ATF_TP_ADD_TC(tp, wcslcpy_heap_end);
+ ATF_TP_ADD_TC(tp, wcslcpy_heap_after_end);
+ ATF_TP_ADD_TC(tp, wcsncpy_before_end);
+ ATF_TP_ADD_TC(tp, wcsncpy_end);
+ ATF_TP_ADD_TC(tp, wcsncpy_heap_before_end);
+ ATF_TP_ADD_TC(tp, wcsncpy_heap_end);
+ ATF_TP_ADD_TC(tp, wcsncpy_heap_after_end);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/secure/generate-fortify-tests.lua b/lib/libc/tests/secure/generate-fortify-tests.lua
new file mode 100755
index 000000000000..c9cd9353a869
--- /dev/null
+++ b/lib/libc/tests/secure/generate-fortify-tests.lua
@@ -0,0 +1,1581 @@
+#!/usr/libexec/flua
+--
+-- SPDX-License-Identifier: BSD-2-Clause
+--
+-- Copyright (c) 2024, Klara, Inc.
+--
+-- Redistribution and use in source and binary forms, with or without
+-- modification, are permitted provided that the following conditions
+-- are met:
+-- 1. Redistributions of source code must retain the above copyright
+-- notice, this list of conditions and the following disclaimer.
+-- 2. Redistributions in binary form must reproduce the above copyright
+-- notice, this 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.
+--
+
+-- THEORY OF OPERATION
+--
+-- generate-fortify-tests.lua is intended to test fortified functions as found
+-- mostly in the various headers in /usr/include/ssp. Each fortified function
+-- gets three basic tests:
+--
+-- 1. Write just before the end of the buffer,
+-- 2. Write right at the end of the buffer,
+-- 3. Write just after the end of the buffer.
+--
+-- Each test is actually generated twice: once with a buffer on the stack, and
+-- again with a buffer on the heap, to confirm that __builtin_object_size(3) can
+-- deduce the buffer size in both scenarios. The tests work by setting up the
+-- stack with our buffer (and some padding on either side to avoid tripping any
+-- other stack or memory protection), doing any initialization as described by
+-- the test definition, then calling the fortified function with the buffer as
+-- outlined by the test definition.
+--
+-- For the 'before' and 'at' the end tests, we're ensuring that valid writes
+-- that are on the verge of being invalid aren't accidentally being detected as
+-- invalid.
+--
+-- The 'after' test is the one that actually tests the functional benefit of
+-- _FORTIFY_SOURCE by violating a boundary that should trigger an abort. As
+-- such, this test differs more from the other two in that it has to fork() off
+-- the fortified function call so that we can monitor for a SIGABRT and
+-- pass/fail the test at function end appropriately.
+
+-- Some tests, like the FD_*() macros, may define these differently. For
+-- instance, for fd sets we're varying the index we pass and not using arbitrary
+-- buffers. Other tests that don't use the length in any way may physically
+-- vary the buffer size for each test case when we'd typically vary the length
+-- we're requesting a write for.
+
+local includes = {
+ "sys/param.h",
+ "sys/jail.h",
+ "sys/random.h",
+ "sys/resource.h",
+ "sys/select.h",
+ "sys/socket.h",
+ "sys/time.h",
+ "sys/uio.h",
+ "sys/wait.h",
+ "dirent.h",
+ "errno.h",
+ "fcntl.h",
+ "limits.h",
+ "poll.h",
+ "signal.h",
+ "stdio.h",
+ "stdlib.h",
+ "string.h",
+ "strings.h",
+ "sysexits.h",
+ "unistd.h",
+ "wchar.h",
+ "atf-c.h",
+}
+
+local tests_added = {}
+
+-- Configuration for tests that want the host/domainname
+local hostname = "host.example.com"
+local domainname = "example.com"
+
+-- Some of these will need to be excluded because clang sees the wrong size when
+-- an array is embedded inside a struct, we'll get something that looks more
+-- like __builtin_object_size(ptr, 0) than it does the correct
+-- __builtin_object_size(ptr, 1) (i.e., includes the padding after). This is
+-- almost certainly a bug in llvm.
+local function excludes_stack_overflow(disposition, is_heap)
+ return (not is_heap) and disposition > 0
+end
+
+local poll_init = [[
+ for (size_t i = 0; i < howmany(__bufsz, sizeof(struct pollfd)); i++) {
+ __stack.__buf[i].fd = -1;
+ }
+]]
+
+local printf_stackvars = "\tchar srcvar[__len + 10];\n"
+local printf_init = [[
+ memset(srcvar, 'A', sizeof(srcvar) - 1);
+ srcvar[sizeof(srcvar) - 1] = '\0';
+]]
+
+local readv_stackvars = "\tstruct iovec iov[1];\n"
+local readv_init = [[
+ iov[0].iov_base = __stack.__buf;
+ iov[0].iov_len = __len;
+
+ replace_stdin();
+]]
+
+local socket_stackvars = "\tint sock[2] = { -1, -1 };\n"
+local recvfrom_sockaddr_stackvars = socket_stackvars .. [[
+ char data[16];
+ socklen_t socklen;
+]]
+local recvmsg_stackvars = socket_stackvars .. "\tstruct msghdr msg;\n"
+local socket_init = [[
+ new_socket(sock);
+]]
+local socket_socklen_init = socket_init .. [[
+ socklen = __len;
+]]
+
+local stdio_init = [[
+ replace_stdin();
+]]
+
+local string_stackvars = "\tchar src[__len];\n"
+local string_init = [[
+ memset(__stack.__buf, 0, __len);
+ memset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+]]
+
+local wstring_stackvars = "\twchar_t src[__len];\n"
+local wstring_init = [[
+ wmemset(__stack.__buf, 0, __len);
+ wmemset(src, 'A', __len - 1);
+ src[__len - 1] = '\0';
+]]
+
+-- Each test entry describes how to test a given function. We need to know how
+-- to construct the buffer, we need to know the argument set we're dealing with,
+-- and we need to know what we're passing to each argument. We could be passing
+-- fixed values, or we could be passing the __buf under test.
+--
+-- definition:
+-- func: name of the function under test to call
+-- bufsize: size of buffer to generate, defaults to 42
+-- buftype: type of buffer to generate, defaults to unsigned char[]
+-- arguments: __buf, __len, or the name of a variable placed on the stack
+-- exclude: a function(disposition, is_heap) that returns true if this combo
+-- should be excluded.
+-- stackvars: extra variables to be placed on the stack, should be a string
+-- optionally formatted with tabs and newlines
+-- init: extra code to inject just before the function call for initialization
+-- of the buffer or any of the above-added stackvars; also a string
+-- uses_len: bool-ish, necessary if arguments doesn't include either __idx or
+-- or __len so that the test generator doesn't try to vary the size of the
+-- buffer instead of just manipulating __idx/__len to try and induce an
+-- overflow.
+--
+-- Most tests will just use the default bufsize/buftype, but under some
+-- circumstances it's useful to use a different type (e.g., for alignment
+-- requirements).
+local all_tests = {
+ random = {
+ -- <sys/random.h>
+ {
+ func = "getrandom",
+ arguments = {
+ "__buf",
+ "__len",
+ "0",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ },
+ select = {
+ -- <sys/select.h>
+ {
+ func = "FD_SET",
+ bufsize = "FD_SETSIZE",
+ buftype = "fd_set",
+ arguments = {
+ "__idx",
+ "__buf",
+ },
+ },
+ {
+ func = "FD_CLR",
+ bufsize = "FD_SETSIZE",
+ buftype = "fd_set",
+ arguments = {
+ "__idx",
+ "__buf",
+ },
+ },
+ {
+ func = "FD_ISSET",
+ bufsize = "FD_SETSIZE",
+ buftype = "fd_set",
+ arguments = {
+ "__idx",
+ "__buf",
+ },
+ },
+ },
+ socket = {
+ -- <sys/socket.h>
+ {
+ func = "getpeername",
+ buftype = "struct sockaddr",
+ bufsize = "sizeof(struct sockaddr)",
+ arguments = {
+ "sock[0]",
+ "__buf",
+ "&socklen",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = socket_stackvars .. "\tsocklen_t socklen;",
+ init = socket_socklen_init,
+ uses_len = true,
+ },
+ {
+ func = "getsockname",
+ buftype = "struct sockaddr",
+ bufsize = "sizeof(struct sockaddr)",
+ arguments = {
+ "sock[0]",
+ "__buf",
+ "&socklen",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = socket_stackvars .. "\tsocklen_t socklen;",
+ init = socket_socklen_init,
+ uses_len = true,
+ },
+ {
+ func = "recv",
+ arguments = {
+ "sock[0]",
+ "__buf",
+ "__len",
+ "0",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = socket_stackvars,
+ init = socket_init,
+ },
+ {
+ func = "recvfrom",
+ arguments = {
+ "sock[0]",
+ "__buf",
+ "__len",
+ "0",
+ "NULL",
+ "NULL",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = socket_stackvars,
+ init = socket_init,
+ },
+ {
+ func = "recvfrom",
+ variant = "sockaddr",
+ buftype = "struct sockaddr",
+ bufsize = "sizeof(struct sockaddr)",
+ arguments = {
+ "sock[0]",
+ "data",
+ "sizeof(data)",
+ "0",
+ "__buf",
+ "&socklen",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = recvfrom_sockaddr_stackvars,
+ init = socket_socklen_init,
+ uses_len = true,
+ },
+ {
+ func = "recvmsg",
+ variant = "msg_name",
+ buftype = "struct sockaddr",
+ bufsize = "sizeof(struct sockaddr)",
+ arguments = {
+ "sock[0]",
+ "&msg",
+ "0",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = recvmsg_stackvars,
+ init = [[
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = BUF;
+ msg.msg_namelen = __len;
+]],
+ uses_len = true,
+ },
+ {
+ func = "recvmsg",
+ variant = "msg_iov",
+ arguments = {
+ "sock[0]",
+ "&msg",
+ "0",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = recvmsg_stackvars .. "\tstruct iovec iov[2];\n",
+ init = [[
+ memset(&msg, 0, sizeof(msg));
+ memset(&iov[0], 0, sizeof(iov));
+
+ /*
+ * We position the buffer second just so that we can confirm that the
+ * fortification bits are traversing the iovec correctly.
+ */
+ iov[1].iov_base = BUF;
+ iov[1].iov_len = __len;
+
+ msg.msg_iov = &iov[0];
+ msg.msg_iovlen = nitems(iov);
+]],
+ uses_len = true,
+ },
+ {
+ func = "recvmsg",
+ variant = "msg_control",
+ bufsize = "CMSG_SPACE(sizeof(int))",
+ arguments = {
+ "sock[0]",
+ "&msg",
+ "0",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = recvmsg_stackvars,
+ init = [[
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_control = BUF;
+ msg.msg_controllen = __len;
+]],
+ uses_len = true,
+ },
+ {
+ func = "recvmmsg",
+ variant = "msgvec",
+ buftype = "struct mmsghdr[]",
+ bufsize = "2",
+ arguments = {
+ "sock[0]",
+ "__buf",
+ "__len",
+ "0",
+ "NULL",
+ },
+ stackvars = socket_stackvars,
+ },
+ {
+ -- We'll assume that recvmsg is covering msghdr
+ -- validation thoroughly enough, we'll just try tossing
+ -- an error in the second element of a msgvec to try and
+ -- make sure that each one is being validated.
+ func = "recvmmsg",
+ variant = "msghdr",
+ arguments = {
+ "sock[0]",
+ "&msgvec[0]",
+ "nitems(msgvec)",
+ "0",
+ "NULL",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = socket_stackvars .. "\tstruct mmsghdr msgvec[2];\n",
+ init = [[
+ memset(&msgvec[0], 0, sizeof(msgvec));
+
+ /*
+ * Same as above, make sure fortification isn't ignoring n > 1 elements
+ * of the msgvec.
+ */
+ msgvec[1].msg_hdr.msg_control = BUF;
+ msgvec[1].msg_hdr.msg_controllen = __len;
+]],
+ uses_len = true,
+ },
+ },
+ uio = {
+ -- <sys/uio.h>
+ {
+ func = "readv",
+ buftype = "struct iovec[]",
+ bufsize = 2,
+ arguments = {
+ "STDIN_FILENO",
+ "__buf",
+ "__len",
+ },
+ },
+ {
+ func = "readv",
+ variant = "iov",
+ arguments = {
+ "STDIN_FILENO",
+ "iov",
+ "nitems(iov)",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = readv_stackvars,
+ init = readv_init,
+ uses_len = true,
+ },
+ {
+ func = "preadv",
+ buftype = "struct iovec[]",
+ bufsize = 2,
+ arguments = {
+ "STDIN_FILENO",
+ "__buf",
+ "__len",
+ "0",
+ },
+ },
+ {
+ func = "preadv",
+ variant = "iov",
+ arguments = {
+ "STDIN_FILENO",
+ "iov",
+ "nitems(iov)",
+ "0",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = readv_stackvars,
+ init = readv_init,
+ uses_len = true,
+ },
+ },
+ poll = {
+ -- <poll.h>
+ {
+ func = "poll",
+ bufsize = "4",
+ buftype = "struct pollfd[]",
+ arguments = {
+ "__buf",
+ "__len",
+ "0",
+ },
+ init = poll_init,
+ },
+ {
+ func = "ppoll",
+ bufsize = "4",
+ buftype = "struct pollfd[]",
+ arguments = {
+ "__buf",
+ "__len",
+ "&tv",
+ "NULL",
+ },
+ stackvars = "\tstruct timespec tv = { 0 };\n",
+ init = poll_init,
+ },
+ },
+ signal = {
+ -- <signal.h>
+ {
+ func = "sig2str",
+ bufsize = "SIG2STR_MAX",
+ arguments = {
+ "1",
+ "__buf",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ },
+ stdio = {
+ -- <stdio.h>
+ {
+ func = "ctermid",
+ bufsize = "L_ctermid",
+ arguments = {
+ "__buf",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ {
+ func = "ctermid_r",
+ bufsize = "L_ctermid",
+ arguments = {
+ "__buf",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ {
+ func = "fread",
+ arguments = {
+ "__buf",
+ "__len",
+ "1",
+ "stdin",
+ },
+ exclude = excludes_stack_overflow,
+ init = stdio_init,
+ },
+ {
+ func = "fread_unlocked",
+ arguments = {
+ "__buf",
+ "__len",
+ "1",
+ "stdin",
+ },
+ exclude = excludes_stack_overflow,
+ init = stdio_init,
+ },
+ {
+ func = "gets_s",
+ arguments = {
+ "__buf",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ init = stdio_init,
+ },
+ {
+ func = "sprintf",
+ arguments = {
+ "__buf",
+ "\"%.*s\"",
+ "(int)__len - 1", -- - 1 for NUL terminator
+ "srcvar",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = printf_stackvars,
+ init = printf_init,
+ },
+ {
+ func = "snprintf",
+ arguments = {
+ "__buf",
+ "__len",
+ "\"%.*s\"",
+ "(int)__len - 1", -- - 1 for NUL terminator
+ "srcvar",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = printf_stackvars,
+ init = printf_init,
+ },
+ {
+ func = "tmpnam",
+ bufsize = "L_tmpnam",
+ arguments = {
+ "__buf",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ {
+ func = "fgets",
+ arguments = {
+ "__buf",
+ "__len",
+ "fp",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = "\tFILE *fp;\n",
+ init = [[
+ fp = new_fp(__len);
+]],
+ },
+ },
+ stdlib = {
+ -- <stdlib.h>
+ {
+ func = "arc4random_buf",
+ arguments = {
+ "__buf",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ {
+ func = "getenv_r",
+ arguments = {
+ "\"PATH\"",
+ "__buf",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ {
+ func = "realpath",
+ bufsize = "PATH_MAX",
+ arguments = {
+ "\".\"",
+ "__buf",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ },
+ string = {
+ -- <string.h>
+ {
+ func = "memcpy",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = "\tchar src[__len + 10];\n",
+ },
+ {
+ func = "mempcpy",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = "\tchar src[__len + 10];\n",
+ },
+ {
+ func = "memmove",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = "\tchar src[__len + 10];\n",
+ },
+ {
+ func = "memset",
+ arguments = {
+ "__buf",
+ "0",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ {
+ func = "memset_explicit",
+ arguments = {
+ "__buf",
+ "0",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ {
+ func = "stpcpy",
+ arguments = {
+ "__buf",
+ "src",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = string_stackvars,
+ init = string_init,
+ uses_len = true,
+ },
+ {
+ func = "stpncpy",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = string_stackvars,
+ init = string_init,
+ },
+ {
+ func = "strcat",
+ arguments = {
+ "__buf",
+ "src",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = string_stackvars,
+ init = string_init,
+ uses_len = true,
+ },
+ {
+ func = "strlcat",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = string_stackvars,
+ init = string_init,
+ },
+ {
+ func = "strncat",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = string_stackvars,
+ init = string_init,
+ },
+ {
+ func = "strcpy",
+ arguments = {
+ "__buf",
+ "src",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = string_stackvars,
+ init = string_init,
+ uses_len = true,
+ },
+ {
+ func = "strlcpy",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = string_stackvars,
+ init = string_init,
+ },
+ {
+ func = "strncpy",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = string_stackvars,
+ init = string_init,
+ },
+ },
+ strings = {
+ -- <strings.h>
+ {
+ func = "bcopy",
+ arguments = {
+ "src",
+ "__buf",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = "\tchar src[__len + 10];\n",
+ },
+ {
+ func = "bzero",
+ arguments = {
+ "__buf",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ {
+ func = "explicit_bzero",
+ arguments = {
+ "__buf",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ },
+ unistd = {
+ -- <unistd.h>
+ {
+ func = "getcwd",
+ bufsize = "8",
+ arguments = {
+ "__buf",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ {
+ func = "getgrouplist",
+ bufsize = "4",
+ buftype = "gid_t[]",
+ arguments = {
+ "\"root\"",
+ "0",
+ "__buf",
+ "&intlen",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = "\tint intlen = (int)__len;\n",
+ uses_len = true,
+ },
+ {
+ func = "getgroups",
+ bufsize = "4",
+ buftype = "gid_t[]",
+ arguments = {
+ "__len",
+ "__buf",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ {
+ func = "getloginclass",
+ arguments = {
+ "__buf",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ {
+ func = "pread",
+ bufsize = "41",
+ arguments = {
+ "fd",
+ "__buf",
+ "__len",
+ "0",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = "\tint fd;\n",
+ init = [[
+ fd = new_tmpfile(); /* Cannot fail */
+]],
+ },
+ {
+ func = "read",
+ bufsize = "41",
+ arguments = {
+ "fd",
+ "__buf",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = "\tint fd;\n",
+ init = [[
+ fd = new_tmpfile(); /* Cannot fail */
+]],
+ },
+ {
+ func = "readlink",
+ arguments = {
+ "path",
+ "__buf",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = "\tconst char *path;\n",
+ init = [[
+ path = new_symlink(__len); /* Cannot fail */
+]],
+ },
+ {
+ func = "readlinkat",
+ arguments = {
+ "AT_FDCWD",
+ "path",
+ "__buf",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = "\tconst char *path;\n",
+ init = [[
+ path = new_symlink(__len); /* Cannot fail */
+]],
+ },
+ {
+ func = "getdomainname",
+ bufsize = #domainname + 1,
+ arguments = {
+ "__buf",
+ "__len",
+ },
+ need_root = true,
+ exclude = excludes_stack_overflow,
+ early_init = " dhost_jail();",
+ },
+ {
+ func = "getentropy",
+ arguments = {
+ "__buf",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ {
+ func = "gethostname",
+ bufsize = #hostname + 1,
+ arguments = {
+ "__buf",
+ "__len",
+ },
+ need_root = true,
+ exclude = excludes_stack_overflow,
+ early_init = " dhost_jail();",
+ },
+ {
+ func = "getlogin_r",
+ bufsize = "MAXLOGNAME + 1",
+ arguments = {
+ "__buf",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ {
+ func = "ttyname_r",
+ arguments = {
+ "fd",
+ "__buf",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = "\tint fd;\n",
+ early_init = [[
+ fd = STDIN_FILENO;
+ if (!isatty(fd))
+ atf_tc_skip("stdin is not an fd");
+]]
+ },
+ },
+ wchar = {
+ -- <wchar.h>
+ {
+ func = "wmemcpy",
+ buftype = "wchar_t[]",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = "\twchar_t src[__len + 10];\n",
+ },
+ {
+ func = "wmempcpy",
+ buftype = "wchar_t[]",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = "\twchar_t src[__len + 10];\n",
+ },
+ {
+ func = "wmemmove",
+ buftype = "wchar_t[]",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = "\twchar_t src[__len + 10];\n",
+ },
+ {
+ func = "wmemset",
+ buftype = "wchar_t[]",
+ arguments = {
+ "__buf",
+ "L'0'",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ },
+ {
+ func = "wcpcpy",
+ buftype = "wchar_t[]",
+ arguments = {
+ "__buf",
+ "src",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = wstring_stackvars,
+ init = wstring_init,
+ uses_len = true,
+ },
+ {
+ func = "wcpncpy",
+ buftype = "wchar_t[]",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = wstring_stackvars,
+ init = wstring_init,
+ },
+ {
+ func = "wcscat",
+ buftype = "wchar_t[]",
+ arguments = {
+ "__buf",
+ "src",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = wstring_stackvars,
+ init = wstring_init,
+ uses_len = true,
+ },
+ {
+ func = "wcslcat",
+ buftype = "wchar_t[]",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = wstring_stackvars,
+ init = wstring_init,
+ },
+ {
+ func = "wcsncat",
+ buftype = "wchar_t[]",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = wstring_stackvars,
+ init = wstring_init,
+ },
+ {
+ func = "wcscpy",
+ buftype = "wchar_t[]",
+ arguments = {
+ "__buf",
+ "src",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = wstring_stackvars,
+ init = wstring_init,
+ uses_len = true,
+ },
+ {
+ func = "wcslcpy",
+ buftype = "wchar_t[]",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = wstring_stackvars,
+ init = wstring_init,
+ },
+ {
+ func = "wcsncpy",
+ buftype = "wchar_t[]",
+ arguments = {
+ "__buf",
+ "src",
+ "__len",
+ },
+ exclude = excludes_stack_overflow,
+ stackvars = wstring_stackvars,
+ init = wstring_init,
+ },
+ },
+}
+
+local function write_test_boilerplate(fh, name, body, def)
+ fh:write("ATF_TC(" .. name .. ");\n")
+ fh:write("ATF_TC_HEAD(" .. name .. ", tc)\n")
+ fh:write("{\n")
+ if def.need_root then
+ fh:write(" atf_tc_set_md_var(tc, \"require.user\", \"root\");\n")
+ end
+ fh:write("}\n")
+
+ fh:write("ATF_TC_BODY(" .. name .. ", tc)\n")
+ fh:write("{\n" .. body .. "\n}\n\n")
+ return name
+end
+
+local function generate_test_name(func, variant, disposition, heap)
+ local basename = func
+ if variant then
+ basename = basename .. "_" .. variant
+ end
+ if heap then
+ basename = basename .. "_heap"
+ end
+ if disposition < 0 then
+ return basename .. "_before_end"
+ elseif disposition == 0 then
+ return basename .. "_end"
+ else
+ return basename .. "_after_end"
+ end
+end
+
+local function array_type(buftype)
+ if not buftype:match("%[%]") then
+ return nil
+ end
+
+ return buftype:gsub("%[%]", "")
+end
+
+local function configurable(def, idx)
+ local cfgitem = def[idx]
+
+ if not cfgitem then
+ return nil
+ end
+
+ if type(cfgitem) == "function" then
+ return cfgitem()
+ end
+
+ return cfgitem
+end
+
+local function generate_stackframe(buftype, bufsize, disposition, heap, def)
+ local function len_offset(inverted)
+ -- Tests that don't use __len in their arguments may use an
+ -- inverted sense because we can't just specify a length that
+ -- would induce an access just after the end. Instead, we have
+ -- to manipulate the buffer size to be too short so that the
+ -- function under test would write one too many.
+ if disposition < 0 then
+ return ((inverted and " + ") or " - ") .. "1"
+ elseif disposition == 0 then
+ return ""
+ else
+ return ((inverted and " - ") or " + ") .. "1"
+ end
+ end
+
+ local function test_uses_len()
+ if def.uses_len then
+ return true
+ end
+
+ for _, arg in ipairs(def.arguments) do
+ if arg:match("__len") or arg:match("__idx") then
+ return true
+ end
+ end
+
+ return false
+ end
+
+
+ -- This is perhaps a little convoluted, but we toss the buffer into a
+ -- struct on the stack to guarantee that we have at least one valid
+ -- byte on either side of the buffer -- a measure to make sure that
+ -- we're tripping _FORTIFY_SOURCE specifically in the buffer + 1 case,
+ -- rather than some other stack or memory protection.
+ local vars = "\tstruct {\n"
+ vars = vars .. "\t\tuint8_t padding_l;\n"
+
+ local uses_len = test_uses_len()
+ local bufsize_offset = len_offset(not uses_len)
+ local buftype_elem = array_type(buftype)
+ local size_expr = bufsize
+
+ if not uses_len then
+ -- If the length isn't in use, we have to vary the buffer size
+ -- since the fortified function likely has some internal size
+ -- constraint that it's supposed to be checking.
+ size_expr = size_expr .. bufsize_offset
+ end
+
+ if not heap and buftype_elem then
+ -- Array type: size goes after identifier
+ vars = vars .. "\t\t" .. buftype_elem ..
+ " __buf[" .. size_expr .. "];\n"
+ else
+ local basic_type = buftype_elem or buftype
+
+ -- Heap tests obviously just put a pointer on the stack that
+ -- points to our new allocation, but we leave it in the padded
+ -- struct just to simplify our generator.
+ if heap then
+ basic_type = basic_type .. " *"
+ end
+ vars = vars .. "\t\t" .. basic_type .. " __buf;\n"
+ end
+
+ -- padding_r is our just-past-the-end padding that we use to make sure
+ -- that there's a valid portion after the buffer that isn't being
+ -- included in our function calls. If we didn't have it, then we'd have
+ -- a hard time feeling confident that an abort on the just-after tests
+ -- isn't maybe from some other memory or stack protection.
+ vars = vars .. "\t\tuint8_t padding_r;\n"
+ vars = vars .. "\t} __stack;\n"
+
+ -- Not all tests will use __bufsz, but some do for, e.g., clearing
+ -- memory..
+ vars = vars .. "\tconst size_t __bufsz __unused = "
+ if heap then
+ local scalar = 1
+ if buftype_elem then
+ scalar = size_expr
+ end
+
+ vars = vars .. "sizeof(*__stack.__buf) * (" .. scalar .. ");\n"
+ else
+ vars = vars .. "sizeof(__stack.__buf);\n"
+ end
+
+ vars = vars .. "\tconst size_t __len = " .. bufsize ..
+ bufsize_offset .. ";\n"
+ vars = vars .. "\tconst size_t __idx __unused = __len - 1;\n"
+
+ -- For overflow testing, we need to fork() because we're expecting the
+ -- test to ultimately abort()/_exit(). Then we can collect the exit
+ -- status and report appropriately.
+ if disposition > 0 then
+ vars = vars .. "\tpid_t __child;\n"
+ vars = vars .. "\tint __status;\n"
+ end
+
+ -- Any other stackvars defined by the test get placed after everything
+ -- else.
+ vars = vars .. (configurable(def, "stackvars") or "")
+
+ return vars
+end
+
+local function write_test(fh, func, disposition, heap, def)
+ local testname = generate_test_name(func, def.variant, disposition, heap)
+ local buftype = def.buftype or "unsigned char[]"
+ local bufsize = def.bufsize or 42
+ local body = ""
+
+ if def.exclude and def.exclude(disposition, heap) then
+ return
+ end
+
+ local function need_addr()
+ return not (buftype:match("%[%]") or buftype:match("%*"))
+ end
+
+ if heap then
+ body = body .. "#define BUF __stack.__buf\n"
+ else
+ body = body .. "#define BUF &__stack.__buf\n"
+ end
+
+ -- Setup the buffer
+ body = body .. generate_stackframe(buftype, bufsize, disposition, heap, def) ..
+ "\n"
+
+ -- Any early initialization goes before we would fork for the just-after
+ -- tests, because they may want to skip the test based on some criteria
+ -- and we can't propagate that up very easily once we're forked.
+ local early_init = configurable(def, "early_init")
+ body = body .. (early_init or "")
+ if early_init then
+ body = body .. "\n"
+ end
+
+ -- Fork off, iff we're testing some access past the end of the buffer.
+ if disposition > 0 then
+ body = body .. [[
+ __child = fork();
+ ATF_REQUIRE(__child >= 0);
+ if (__child > 0)
+ goto monitor;
+
+ /* Child */
+ disable_coredumps();
+]]
+ end
+
+ local bufvar = "__stack.__buf"
+ if heap then
+ -- Buffer needs to be initialized because it's a heap allocation.
+ body = body .. "\t" .. bufvar .. " = malloc(__bufsz);\n"
+ end
+
+ -- Non-early init happens just after the fork in the child, not the
+ -- monitor. This is used to setup any other buffers we may need, for
+ -- instance.
+ local extra_init = configurable(def, "init")
+ body = body .. (extra_init or "")
+
+ if heap or extra_init then
+ body = body .. "\n"
+ end
+
+ -- Setup the function call with arguments as described in the test
+ -- definition.
+ body = body .. "\t" .. func .. "("
+
+ for idx, arg in ipairs(def.arguments) do
+ if idx > 1 then
+ body = body .. ", "
+ end
+
+ if arg == "__buf" then
+ if not heap and need_addr() then
+ body = body .. "&"
+ end
+
+ body = body .. bufvar
+ else
+ local argname = arg
+
+ if def.value_of then
+ argname = argname or def.value_of(arg)
+ end
+
+ body = body .. argname
+ end
+ end
+
+ body = body .. ");\n"
+
+ -- Monitor stuff follows, for OOB access.
+ if disposition <= 0 then
+ goto skip
+ end
+
+ body = body .. [[
+ _exit(EX_SOFTWARE); /* Should have aborted. */
+
+monitor:
+ while (waitpid(__child, &__status, 0) != __child) {
+ ATF_REQUIRE_EQ(EINTR, errno);
+ }
+
+ if (!WIFSIGNALED(__status)) {
+ switch (WEXITSTATUS(__status)) {
+ case EX_SOFTWARE:
+ atf_tc_fail("FORTIFY_SOURCE failed to abort");
+ break;
+ case EX_OSERR:
+ atf_tc_fail("setrlimit(2) failed");
+ break;
+ default:
+ atf_tc_fail("child exited with status %d",
+ WEXITSTATUS(__status));
+ }
+ } else {
+ ATF_REQUIRE_EQ(SIGABRT, WTERMSIG(__status));
+ }
+]]
+
+::skip::
+ body = body .. "#undef BUF\n"
+ return write_test_boilerplate(fh, testname, body, def)
+end
+
+-- main()
+local tests
+local tcat = assert(arg[1], "usage: generate-fortify-tests.lua <category>")
+for k, defs in pairs(all_tests) do
+ if k == tcat then
+ tests = defs
+ break
+ end
+end
+
+assert(tests, "category " .. tcat .. " not found")
+
+local fh = io.stdout
+fh:write("/* @" .. "generated" .. " by `generate-fortify-tests.lua \"" ..
+ tcat .. "\"` */\n\n")
+fh:write("#define _FORTIFY_SOURCE 2\n")
+fh:write("#define TMPFILE_SIZE (1024 * 32)\n")
+
+fh:write("\n")
+for _, inc in ipairs(includes) do
+ fh:write("#include <" .. inc .. ">\n")
+end
+
+fh:write([[
+
+static FILE * __unused
+new_fp(size_t __len)
+{
+ static char fpbuf[LINE_MAX];
+ FILE *fp;
+
+ ATF_REQUIRE(__len <= sizeof(fpbuf));
+
+ memset(fpbuf, 'A', sizeof(fpbuf) - 1);
+ fpbuf[sizeof(fpbuf) - 1] = '\0';
+
+ fp = fmemopen(fpbuf, sizeof(fpbuf), "rb");
+ ATF_REQUIRE(fp != NULL);
+
+ return (fp);
+}
+
+/*
+ * Create a new symlink to use for readlink(2) style tests, we'll just use a
+ * random target name to have something interesting to look at.
+ */
+static const char * __unused
+new_symlink(size_t __len)
+{
+ static const char linkname[] = "link";
+ char target[MAXNAMLEN];
+ int error;
+
+ ATF_REQUIRE(__len <= sizeof(target));
+
+ arc4random_buf(target, sizeof(target));
+
+ error = unlink(linkname);
+ ATF_REQUIRE(error == 0 || errno == ENOENT);
+
+ error = symlink(target, linkname);
+ ATF_REQUIRE(error == 0);
+
+ return (linkname);
+}
+
+/*
+ * For our purposes, first descriptor will be the reader; we'll send both
+ * raw data and a control message over it so that the result can be used for
+ * any of our recv*() tests.
+ */
+static void __unused
+new_socket(int sock[2])
+{
+ unsigned char ctrl[CMSG_SPACE(sizeof(int))] = { 0 };
+ static char sockbuf[256];
+ ssize_t rv;
+ size_t total = 0;
+ struct msghdr hdr = { 0 };
+ struct cmsghdr *cmsg;
+ int error, fd;
+
+ error = socketpair(AF_UNIX, SOCK_STREAM, 0, sock);
+ ATF_REQUIRE(error == 0);
+
+ while (total != sizeof(sockbuf)) {
+ rv = send(sock[1], &sockbuf[total], sizeof(sockbuf) - total, 0);
+
+ ATF_REQUIRE_MSG(rv > 0,
+ "expected bytes sent, got %zd with %zu left (size %zu, total %zu)",
+ rv, sizeof(sockbuf) - total, sizeof(sockbuf), total);
+ ATF_REQUIRE_MSG(total + (size_t)rv <= sizeof(sockbuf),
+ "%zd exceeds total %zu", rv, sizeof(sockbuf));
+ total += rv;
+ }
+
+ hdr.msg_control = ctrl;
+ hdr.msg_controllen = sizeof(ctrl);
+
+ cmsg = CMSG_FIRSTHDR(&hdr);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+ fd = STDIN_FILENO;
+ memcpy(CMSG_DATA(cmsg), &fd, sizeof(fd));
+
+ error = sendmsg(sock[1], &hdr, 0);
+ ATF_REQUIRE(error != -1);
+}
+
+/*
+ * Constructs a tmpfile that we can use for testing read(2) and friends.
+ */
+static int __unused
+new_tmpfile(void)
+{
+ char buf[1024];
+ ssize_t rv;
+ size_t written;
+ int fd;
+
+ fd = open("tmpfile", O_RDWR | O_CREAT | O_TRUNC, 0644);
+ ATF_REQUIRE(fd >= 0);
+
+ written = 0;
+ while (written < TMPFILE_SIZE) {
+ rv = write(fd, buf, sizeof(buf));
+ ATF_REQUIRE(rv > 0);
+
+ written += rv;
+ }
+
+ ATF_REQUIRE_EQ(0, lseek(fd, 0, SEEK_SET));
+ return (fd);
+}
+
+static void
+disable_coredumps(void)
+{
+ struct rlimit rl = { 0 };
+
+ if (setrlimit(RLIMIT_CORE, &rl) == -1)
+ _exit(EX_OSERR);
+}
+
+/*
+ * Replaces stdin with a file that we can actually read from, for tests where
+ * we want a FILE * or fd that we can get data from.
+ */
+static void __unused
+replace_stdin(void)
+{
+ int fd;
+
+ fd = new_tmpfile();
+
+ (void)dup2(fd, STDIN_FILENO);
+ if (fd != STDIN_FILENO)
+ close(fd);
+}
+
+]])
+
+if tcat == "unistd" then
+ fh:write("#define JAIL_HOSTNAME \"" .. hostname .. "\"\n")
+ fh:write("#define JAIL_DOMAINNAME \"" .. domainname .. "\"\n")
+ fh:write([[
+static void
+dhost_jail(void)
+{
+ struct iovec iov[4];
+ int jid;
+
+ iov[0].iov_base = __DECONST(char *, "host.hostname");
+ iov[0].iov_len = sizeof("host.hostname");
+ iov[1].iov_base = __DECONST(char *, JAIL_HOSTNAME);
+ iov[1].iov_len = sizeof(JAIL_HOSTNAME);
+ iov[2].iov_base = __DECONST(char *, "host.domainname");
+ iov[2].iov_len = sizeof("host.domainname");
+ iov[3].iov_base = __DECONST(char *, JAIL_DOMAINNAME);
+ iov[3].iov_len = sizeof(JAIL_DOMAINNAME);
+
+ jid = jail_set(iov, nitems(iov), JAIL_CREATE | JAIL_ATTACH);
+ ATF_REQUIRE_MSG(jid > 0, "Jail creation failed: %s", strerror(errno));
+}
+
+]])
+end
+
+for _, def in pairs(tests) do
+ local func = def.func
+ local function write_tests(heap)
+ -- Dispositions here are relative to the buffer size prescribed
+ -- by the test definition.
+ local dispositions = def.dispositions or { -1, 0, 1 }
+
+ for _, disposition in ipairs(dispositions) do
+ tests_added[#tests_added + 1] = write_test(fh, func, disposition, heap, def)
+ end
+ end
+
+ write_tests(false)
+ write_tests(true)
+end
+
+fh:write("ATF_TP_ADD_TCS(tp)\n")
+fh:write("{\n")
+for idx = 1, #tests_added do
+ fh:write("\tATF_TP_ADD_TC(tp, " .. tests_added[idx] .. ");\n")
+end
+fh:write("\treturn (atf_no_error());\n")
+fh:write("}\n")
diff --git a/lib/libc/tests/setjmp/Makefile b/lib/libc/tests/setjmp/Makefile
new file mode 100644
index 000000000000..8ea8550dfba0
--- /dev/null
+++ b/lib/libc/tests/setjmp/Makefile
@@ -0,0 +1,10 @@
+NETBSD_ATF_TESTS_C= setjmp_test
+NETBSD_ATF_TESTS_C+= threadjmp_test
+
+LIBADD.t_threadjmp+= pthread
+
+WARNS?= 4
+
+.include "../Makefile.netbsd-tests"
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/setjmp/Makefile.depend b/lib/libc/tests/setjmp/Makefile.depend
new file mode 100644
index 000000000000..e89a5c52c82a
--- /dev/null
+++ b/lib/libc/tests/setjmp/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libnetbsd \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/ssp/Makefile b/lib/libc/tests/ssp/Makefile
new file mode 100644
index 000000000000..644d5a6d64a2
--- /dev/null
+++ b/lib/libc/tests/ssp/Makefile
@@ -0,0 +1,60 @@
+.include <bsd.own.mk>
+
+MK_WERROR= no
+WARNS?= 2
+
+CFLAGS.h_raw+= -fstack-protector-all -Wstack-protector
+.if ${COMPILER_TYPE} == "clang"
+# Only use -fsanitize=bounds when using clang. Otherwise we are not able to
+# override the sanitizer runtime libraries to be the ones installed on the
+# target system.
+CFLAGS.h_raw+= -fsanitize=bounds
+LIBADD+=sys
+.elif ${COMPILER_TYPE} == "gcc"
+CFLAGS.h_raw+= --param ssp-buffer-size=1
+LDADD+= -lssp
+.endif
+
+NETBSD_ATF_TESTS_SH= ssp_test
+
+BINDIR= ${TESTSDIR}
+
+PROGS= h_fgets
+PROGS+= h_gets
+PROGS+= h_getcwd
+PROGS+= h_memcpy
+PROGS+= h_memmove
+PROGS+= h_memset
+# XXX: the h_raw/h_read testcases don't cause a SIGABRT with in-tree gcc right
+# now on amd64 when it trips the stack bounds specified in t_ssp.sh . This
+# probably needs to be fixed as it's currently hardcoded.
+.if ${COMPILER_TYPE} == "clang" && !defined(_SKIP_BUILD) && \
+ (!defined(_RECURSING_PROGS) || ${PROG} == "h_raw") && \
+ defined(MK_CLANG) && ${MK_CLANG} == "yes"
+.include "${SRCTOP}/lib/libclang_rt/compiler-rt-vars.mk"
+_libclang_rt_ubsan= ${SYSROOT}${SANITIZER_LIBDIR}/libclang_rt.ubsan_standalone-${CRTARCH}.a
+.if exists(${_libclang_rt_ubsan})
+PROGS+= h_raw
+LDADD.h_raw+= ${SANITIZER_LDFLAGS}
+.else
+.if make(all)
+.info Could not find runtime library ${_libclang_rt_ubsan}, skipping h_raw
+.endif
+.endif
+.endif
+PROGS+= h_read
+PROGS+= h_readlink
+PROGS+= h_snprintf
+PROGS+= h_sprintf
+PROGS+= h_stpcpy
+PROGS+= h_stpncpy
+PROGS+= h_strcat
+PROGS+= h_strcpy
+PROGS+= h_strncat
+PROGS+= h_strncpy
+PROGS+= h_vsnprintf
+PROGS+= h_vsprintf
+
+.include "../Makefile.netbsd-tests"
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/ssp/Makefile.depend b/lib/libc/tests/ssp/Makefile.depend
new file mode 100644
index 000000000000..a3475b45b52e
--- /dev/null
+++ b/lib/libc/tests/ssp/Makefile.depend
@@ -0,0 +1,20 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libclang_rt/ubsan_standalone.host \
+ lib/libcompiler_rt \
+ lib/librt \
+ lib/libthr \
+ lib/msun \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/stdio/Makefile b/lib/libc/tests/stdio/Makefile
new file mode 100644
index 000000000000..6dca927c4fb7
--- /dev/null
+++ b/lib/libc/tests/stdio/Makefile
@@ -0,0 +1,55 @@
+.include <bsd.own.mk>
+
+ATF_TESTS_C+= eintr_test
+ATF_TESTS_C+= fdopen_test
+ATF_TESTS_C+= flushlbuf_test
+ATF_TESTS_C+= fmemopen2_test
+ATF_TESTS_C+= fopen2_test
+ATF_TESTS_C+= freopen_test
+ATF_TESTS_C+= getdelim_test
+ATF_TESTS_C+= gets_s_test
+ATF_TESTS_C+= mkostemp_test
+ATF_TESTS_C+= open_memstream2_test
+ATF_TESTS_C+= open_wmemstream_test
+ATF_TESTS_C+= perror_test
+ATF_TESTS_C+= print_positional_test
+ATF_TESTS_C+= printbasic_test
+ATF_TESTS_C+= printfloat_test
+ATF_TESTS_C+= scanfloat_test
+ATF_TESTS_C+= snprintf_test
+ATF_TESTS_C+= sscanf_test
+ATF_TESTS_C+= swprintf_test
+ATF_TESTS_C+= swscanf_test
+
+SRCS.fopen2_test= fopen_test.c
+
+NETBSD_ATF_TESTS_C= clearerr_test
+NETBSD_ATF_TESTS_C+= fflush_test
+NETBSD_ATF_TESTS_C+= fmemopen_test
+NETBSD_ATF_TESTS_C+= fopen_test
+NETBSD_ATF_TESTS_C+= fputc_test
+NETBSD_ATF_TESTS_C+= mktemp_test
+NETBSD_ATF_TESTS_C+= open_memstream_test
+NETBSD_ATF_TESTS_C+= popen_test
+NETBSD_ATF_TESTS_C+= printf_test
+NETBSD_ATF_TESTS_C+= scanf_test
+
+LIBADD.eintr_test+= md
+LIBADD.printfloat_test+= m
+LIBADD.scanfloat_test+= m
+
+# Older toolchains won't understand C23 %b, %wN, %wfN
+PROG_OVERRIDE_VARS+= NO_WFORMAT
+NO_WFORMAT.snprintf_test=
+NO_WFORMAT.sscanf_test=
+NO_WFORMAT.swprintf_test=
+NO_WFORMAT.swscanf_test=
+
+.if ${COMPILER_TYPE} == "gcc"
+# 90: use of assignment suppression and length modifier together in scanf format
+NO_WFORMAT.scanfloat_test=
+.endif
+
+.include "../Makefile.netbsd-tests"
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/stdio/Makefile.depend b/lib/libc/tests/stdio/Makefile.depend
new file mode 100644
index 000000000000..e5ddbc552be2
--- /dev/null
+++ b/lib/libc/tests/stdio/Makefile.depend
@@ -0,0 +1,19 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libnetbsd \
+ lib/msun \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/stdio/eintr_test.c b/lib/libc/tests/stdio/eintr_test.c
new file mode 100644
index 000000000000..55354c8926c5
--- /dev/null
+++ b/lib/libc/tests/stdio/eintr_test.c
@@ -0,0 +1,143 @@
+/*
+ * Initially written by Yar Tikhiy <yar@freebsd.org> in PR 76398.
+ * Bug fixes and instrumentation by kib@freebsd.org.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <md5.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#define NDATA 1000
+#define DELAY 2
+
+static void
+hup(int signo __unused)
+{
+}
+
+static int ndata, seq;
+
+static void
+setdata(int n)
+{
+ ndata = n;
+ seq = 0;
+}
+
+static char *
+getdata(void)
+{
+ static char databuf[256];
+ static char xeof[] = "#";
+
+ if (seq > ndata)
+ return (NULL);
+ if (seq == ndata) {
+ seq++;
+ return (xeof);
+ }
+ sprintf(databuf, "%08d xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n", seq++);
+ return (databuf);
+}
+
+ATF_TC_WITHOUT_HEAD(eintr_test);
+ATF_TC_BODY(eintr_test, tc)
+{
+ char c, digest0[33], digest[33], *p;
+ FILE *fp;
+ int i, s[2], total0, total;
+ MD5_CTX md5;
+ pid_t child;
+ struct sigaction sa;
+
+ MD5Init(&md5);
+ setdata(NDATA);
+ for (total0 = 0; (p = getdata()) != NULL; total0 += strlen(p))
+ MD5Update(&md5, p, strlen(p));
+ p = MD5End(&md5, digest0);
+
+ sa.sa_handler = hup;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ ATF_REQUIRE(sigaction(SIGHUP, &sa, NULL) == 0);
+
+ ATF_REQUIRE(socketpair(PF_UNIX, SOCK_STREAM, 0, s) == 0);
+
+ switch (child = fork()) {
+ case -1:
+ atf_tc_fail("fork failed %s", strerror(errno));
+ break;
+
+ case 0:
+ ATF_REQUIRE((fp = fdopen(s[0], "w")) != NULL);
+ close(s[1]);
+ setdata(NDATA);
+ while ((p = getdata())) {
+ for (; *p;) {
+ if (fputc(*p, fp) == EOF) {
+ if (errno == EINTR) {
+ clearerr(fp);
+ } else {
+ atf_tc_fail("fputc errno %s",
+ strerror(errno));
+ }
+ } else {
+ p++;
+ }
+ }
+ }
+ fclose(fp);
+ break;
+
+ default:
+ close(s[0]);
+ ATF_REQUIRE((fp = fdopen(s[1], "r")) != NULL);
+ sleep(DELAY);
+ ATF_REQUIRE(kill(child, SIGHUP) != -1);
+ sleep(DELAY);
+ MD5Init(&md5);
+ for (total = 0;;) {
+ i = fgetc(fp);
+ if (i == EOF) {
+ if (errno == EINTR) {
+ clearerr(fp);
+ } else {
+ atf_tc_fail("fgetc errno %s",
+ strerror(errno));
+ }
+ continue;
+ }
+ total++;
+ c = i;
+ MD5Update(&md5, &c, 1);
+ if (i == '#')
+ break;
+ }
+ MD5End(&md5, digest);
+ fclose(fp);
+ ATF_REQUIRE_MSG(total == total0,
+ "Total number of bytes read does not match: %d %d",
+ total, total0);
+ ATF_REQUIRE_MSG(strcmp(digest, digest0) == 0,
+ "Digests do not match %s %s", digest, digest0);
+ break;
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, eintr_test);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdio/fdopen_test.c b/lib/libc/tests/stdio/fdopen_test.c
new file mode 100644
index 000000000000..5abaed7375b6
--- /dev/null
+++ b/lib/libc/tests/stdio/fdopen_test.c
@@ -0,0 +1,222 @@
+/*-
+ * Copyright (c) 2014 Jilles Tjoelker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+static void
+runtest(const char *fname, int intmode, const char *strmode, bool success)
+{
+ FILE *fp;
+ int fd;
+
+ fd = open(fname, intmode);
+ ATF_REQUIRE_MSG(fd != -1,
+ "open(\"%s\", %#x) failed; errno=%d", fname, intmode, errno);
+
+ fp = fdopen(fd, strmode);
+ if (fp == NULL) {
+ close(fd);
+ ATF_REQUIRE_MSG(success == false,
+ "fdopen(open(\"%s\", %#x), \"%s\") succeeded unexpectedly",
+ fname, intmode, strmode);
+ return;
+ }
+ ATF_REQUIRE_MSG(success == true,
+ "fdopen(open(\"%s\", %#x), \"%s\") failed; errno=%d",
+ fname, intmode, strmode, errno);
+ fclose(fp);
+}
+
+ATF_TC_WITHOUT_HEAD(null__O_RDONLY__r_test);
+ATF_TC_BODY(null__O_RDONLY__r_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, O_RDONLY, "r", true);
+}
+
+ATF_TC_WITHOUT_HEAD(null__O_WRONLY__r_test);
+ATF_TC_BODY(null__O_WRONLY__r_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, O_WRONLY, "r", false);
+}
+
+ATF_TC_WITHOUT_HEAD(null__O_RDWR__r_test);
+ATF_TC_BODY(null__O_RDWR__r_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, O_RDWR, "r", true);
+}
+
+ATF_TC_WITHOUT_HEAD(null__O_RDONLY__w_test);
+ATF_TC_BODY(null__O_RDONLY__w_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, O_RDONLY, "w", false);
+}
+
+ATF_TC_WITHOUT_HEAD(null__O_WRONLY__w_test);
+ATF_TC_BODY(null__O_WRONLY__w_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, O_WRONLY, "w", true);
+}
+
+ATF_TC_WITHOUT_HEAD(null__O_RDWR__w_test);
+ATF_TC_BODY(null__O_RDWR__w_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, O_RDWR, "w", true);
+}
+
+ATF_TC_WITHOUT_HEAD(null__O_RDONLY__a_test);
+ATF_TC_BODY(null__O_RDONLY__a_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, O_RDONLY, "a", false);
+}
+
+ATF_TC_WITHOUT_HEAD(null__O_WRONLY__a_test);
+ATF_TC_BODY(null__O_WRONLY__a_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, O_WRONLY, "a", true);
+}
+
+ATF_TC_WITHOUT_HEAD(null__O_RDWR__test);
+ATF_TC_BODY(null__O_RDWR__test, tc)
+{
+
+ runtest(_PATH_DEVNULL, O_RDWR, "a", true);
+}
+
+ATF_TC_WITHOUT_HEAD(null__O_RDONLY__r_append);
+ATF_TC_BODY(null__O_RDONLY__r_append, tc)
+{
+
+ runtest(_PATH_DEVNULL, O_RDONLY, "r+", false);
+}
+
+ATF_TC_WITHOUT_HEAD(null__O_WRONLY__r_append);
+ATF_TC_BODY(null__O_WRONLY__r_append, tc)
+{
+
+ runtest(_PATH_DEVNULL, O_WRONLY, "r+", false);
+}
+
+ATF_TC_WITHOUT_HEAD(null__O_RDWR__r_append);
+ATF_TC_BODY(null__O_RDWR__r_append, tc)
+{
+
+ runtest(_PATH_DEVNULL, O_RDWR, "r+", true);
+}
+
+ATF_TC_WITHOUT_HEAD(null__O_RDONLY__w_append);
+ATF_TC_BODY(null__O_RDONLY__w_append, tc)
+{
+
+ runtest(_PATH_DEVNULL, O_RDONLY, "w+", false);
+}
+
+ATF_TC_WITHOUT_HEAD(null__O_WRONLY__w_append);
+ATF_TC_BODY(null__O_WRONLY__w_append, tc)
+{
+
+ runtest(_PATH_DEVNULL, O_WRONLY, "w+", false);
+}
+
+ATF_TC_WITHOUT_HEAD(null__O_RDWR__w_append);
+ATF_TC_BODY(null__O_RDWR__w_append, tc)
+{
+
+ runtest(_PATH_DEVNULL, O_RDWR, "w+", true);
+}
+
+ATF_TC_WITHOUT_HEAD(sh__O_EXEC__r);
+ATF_TC_BODY(sh__O_EXEC__r, tc)
+{
+
+ runtest("/bin/sh", O_EXEC, "r", false);
+}
+
+ATF_TC_WITHOUT_HEAD(sh__O_EXEC__w);
+ATF_TC_BODY(sh__O_EXEC__w, tc)
+{
+
+ runtest("/bin/sh", O_EXEC, "w", false);
+}
+
+ATF_TC_WITHOUT_HEAD(sh__O_EXEC__r_append);
+ATF_TC_BODY(sh__O_EXEC__r_append, tc)
+{
+
+ runtest("/bin/sh", O_EXEC, "r+", false);
+}
+
+ATF_TC_WITHOUT_HEAD(sh__O_EXEC__w_append);
+ATF_TC_BODY(sh__O_EXEC__w_append, tc)
+{
+
+ runtest("/bin/sh", O_EXEC, "w+", false);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, null__O_RDONLY__r_test);
+ ATF_TP_ADD_TC(tp, null__O_WRONLY__r_test);
+ ATF_TP_ADD_TC(tp, null__O_RDWR__r_test);
+ ATF_TP_ADD_TC(tp, null__O_RDONLY__w_test);
+ ATF_TP_ADD_TC(tp, null__O_WRONLY__w_test);
+ ATF_TP_ADD_TC(tp, null__O_RDWR__w_test);
+ ATF_TP_ADD_TC(tp, null__O_RDONLY__a_test);
+ ATF_TP_ADD_TC(tp, null__O_WRONLY__a_test);
+ ATF_TP_ADD_TC(tp, null__O_RDWR__test);
+ ATF_TP_ADD_TC(tp, null__O_RDONLY__r_append);
+ ATF_TP_ADD_TC(tp, null__O_WRONLY__r_append);
+ ATF_TP_ADD_TC(tp, null__O_RDWR__r_append);
+ ATF_TP_ADD_TC(tp, null__O_RDONLY__w_append);
+ ATF_TP_ADD_TC(tp, null__O_WRONLY__w_append);
+ ATF_TP_ADD_TC(tp, null__O_RDWR__w_append);
+ ATF_TP_ADD_TC(tp, sh__O_EXEC__r);
+ ATF_TP_ADD_TC(tp, sh__O_EXEC__w);
+ ATF_TP_ADD_TC(tp, sh__O_EXEC__r_append);
+ ATF_TP_ADD_TC(tp, sh__O_EXEC__w_append);
+
+ return (atf_no_error());
+}
+
+/*
+ vim:ts=8:cin:sw=8
+ */
diff --git a/lib/libc/tests/stdio/flushlbuf_test.c b/lib/libc/tests/stdio/flushlbuf_test.c
new file mode 100644
index 000000000000..3f8a6378f933
--- /dev/null
+++ b/lib/libc/tests/stdio/flushlbuf_test.c
@@ -0,0 +1,155 @@
+/*-
+ * Copyright (c) 2023 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <errno.h>
+#include <stdio.h>
+
+#include <atf-c.h>
+
+#define BUFSIZE 16
+
+static const char seq[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+
+struct stream {
+ char buf[BUFSIZE];
+ unsigned int len;
+ unsigned int pos;
+};
+
+static int
+writefn(void *cookie, const char *buf, int len)
+{
+ struct stream *s = cookie;
+ int written = 0;
+
+ if (len <= 0)
+ return (0);
+ while (len > 0 && s->pos < s->len) {
+ s->buf[s->pos++] = *buf++;
+ written++;
+ len--;
+ }
+ if (written > 0)
+ return (written);
+ errno = EAGAIN;
+ return (-1);
+}
+
+ATF_TC_WITHOUT_HEAD(flushlbuf_partial);
+ATF_TC_BODY(flushlbuf_partial, tc)
+{
+ static struct stream s;
+ static char buf[BUFSIZE + 1];
+ FILE *f;
+ unsigned int i = 0;
+ int ret = 0;
+
+ /*
+ * Create the stream and its buffer, print just enough characters
+ * to the stream to fill the buffer without triggering a flush,
+ * then check the state.
+ */
+ s.len = BUFSIZE / 2; // write will fail after this amount
+ ATF_REQUIRE((f = fwopen(&s, writefn)) != NULL);
+ ATF_REQUIRE(setvbuf(f, buf, _IOLBF, BUFSIZE) == 0);
+ while (i < BUFSIZE)
+ if ((ret = fprintf(f, "%c", seq[i++])) < 0)
+ break;
+ ATF_CHECK_EQ(BUFSIZE, i);
+ ATF_CHECK_EQ(seq[i - 1], buf[BUFSIZE - 1]);
+ ATF_CHECK_EQ(1, ret);
+ ATF_CHECK_EQ(0, s.pos);
+
+ /*
+ * At this point, the buffer is full but writefn() has not yet
+ * been called. The next fprintf() call will trigger a preemptive
+ * fflush(), and writefn() will consume s.len characters before
+ * returning EAGAIN, causing fprintf() to fail without having
+ * written anything (which is why we don't increment i here).
+ */
+ ret = fprintf(f, "%c", seq[i]);
+ ATF_CHECK_ERRNO(EAGAIN, ret < 0);
+ ATF_CHECK_EQ(s.len, s.pos);
+
+ /*
+ * We have consumed s.len characters from the buffer, so continue
+ * printing until it is full again and check that no overflow has
+ * occurred yet.
+ */
+ while (i < BUFSIZE + s.len)
+ fprintf(f, "%c", seq[i++]);
+ ATF_CHECK_EQ(BUFSIZE + s.len, i);
+ ATF_CHECK_EQ(seq[i - 1], buf[BUFSIZE - 1]);
+ ATF_CHECK_EQ(0, buf[BUFSIZE]);
+
+ /*
+ * The straw that breaks the camel's back: libc fails to recognize
+ * that the buffer is full and continues to write beyond its end.
+ */
+ fprintf(f, "%c", seq[i++]);
+ ATF_CHECK_EQ(0, buf[BUFSIZE]);
+}
+
+ATF_TC_WITHOUT_HEAD(flushlbuf_full);
+ATF_TC_BODY(flushlbuf_full, tc)
+{
+ static struct stream s;
+ static char buf[BUFSIZE];
+ FILE *f;
+ unsigned int i = 0;
+ int ret = 0;
+
+ /*
+ * Create the stream and its buffer, print just enough characters
+ * to the stream to fill the buffer without triggering a flush,
+ * then check the state.
+ */
+ s.len = 0; // any attempt to write will fail
+ ATF_REQUIRE((f = fwopen(&s, writefn)) != NULL);
+ ATF_REQUIRE(setvbuf(f, buf, _IOLBF, BUFSIZE) == 0);
+ while (i < BUFSIZE)
+ if ((ret = fprintf(f, "%c", seq[i++])) < 0)
+ break;
+ ATF_CHECK_EQ(BUFSIZE, i);
+ ATF_CHECK_EQ(seq[i - 1], buf[BUFSIZE - 1]);
+ ATF_CHECK_EQ(1, ret);
+ ATF_CHECK_EQ(0, s.pos);
+
+ /*
+ * At this point, the buffer is full but writefn() has not yet
+ * been called. The next fprintf() call will trigger a preemptive
+ * fflush(), and writefn() will immediately return EAGAIN, causing
+ * fprintf() to fail without having written anything (which is why
+ * we don't increment i here).
+ */
+ ret = fprintf(f, "%c", seq[i]);
+ ATF_CHECK_ERRNO(EAGAIN, ret < 0);
+ ATF_CHECK_EQ(s.len, s.pos);
+
+ /*
+ * Now make our stream writeable.
+ */
+ s.len = sizeof(s.buf);
+
+ /*
+ * Flush the stream again. The data we failed to write previously
+ * should still be in the buffer and will now be written to the
+ * stream.
+ */
+ ATF_CHECK_EQ(0, fflush(f));
+ ATF_CHECK_EQ(seq[0], s.buf[0]);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, flushlbuf_partial);
+ ATF_TP_ADD_TC(tp, flushlbuf_full);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdio/fmemopen2_test.c b/lib/libc/tests/stdio/fmemopen2_test.c
new file mode 100644
index 000000000000..a558ff3515e9
--- /dev/null
+++ b/lib/libc/tests/stdio/fmemopen2_test.c
@@ -0,0 +1,318 @@
+/*-
+Copyright (C) 2013 Pietro Cerutti <gahr@FreeBSD.org>
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY 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 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.
+*/
+
+/*
+ * Test basic FILE * functions (fread, fwrite, fseek, fclose) against
+ * a FILE * retrieved using fmemopen()
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(test_preexisting);
+ATF_TC_BODY(test_preexisting, tc)
+{
+ /* Use a pre-existing buffer. */
+ char buf[512];
+ char buf2[512];
+ char str[] = "Test writing some stuff";
+ char str2[] = "AAAAAAAAA";
+ char str3[] = "AAAA writing some stuff";
+ FILE *fp;
+ size_t nofw, nofr;
+ int rc;
+
+ /* Open a FILE * using fmemopen. */
+ fp = fmemopen(buf, sizeof(buf), "w");
+ ATF_REQUIRE(fp != NULL);
+
+ /* Write to the buffer. */
+ nofw = fwrite(str, 1, sizeof(str), fp);
+ ATF_REQUIRE(nofw == sizeof(str));
+
+ /* Close the FILE *. */
+ rc = fclose(fp);
+ ATF_REQUIRE(rc == 0);
+
+ /* Re-open the FILE * to read back the data. */
+ fp = fmemopen(buf, sizeof(buf), "r");
+ ATF_REQUIRE(fp != NULL);
+
+ /* Read from the buffer. */
+ bzero(buf2, sizeof(buf2));
+ nofr = fread(buf2, 1, sizeof(buf2), fp);
+ ATF_REQUIRE(nofr == sizeof(buf2));
+
+ /*
+ * Since a write on a FILE * retrieved by fmemopen
+ * will add a '\0' (if there's space), we can check
+ * the strings for equality.
+ */
+ ATF_REQUIRE(strcmp(str, buf2) == 0);
+
+ /* Close the FILE *. */
+ rc = fclose(fp);
+ ATF_REQUIRE(rc == 0);
+
+ /* Now open a FILE * on the first 4 bytes of the string. */
+ fp = fmemopen(str, 4, "w");
+ ATF_REQUIRE(fp != NULL);
+
+ /*
+ * Try to write more bytes than we shoud, we'll get a short count (4).
+ */
+ nofw = fwrite(str2, 1, sizeof(str2), fp);
+ ATF_REQUIRE(nofw == 4);
+
+ /* Close the FILE *. */
+ rc = fclose(fp);
+ ATF_REQUIRE(rc == 0);
+
+ /* Check that the string was not modified after the first 4 bytes. */
+ ATF_REQUIRE(strcmp(str, str3) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(test_autoalloc);
+ATF_TC_BODY(test_autoalloc, tc)
+{
+ /* Let fmemopen allocate the buffer. */
+ FILE *fp;
+ long pos;
+ size_t nofw, i;
+ int rc;
+
+ /* Open a FILE * using fmemopen. */
+ fp = fmemopen(NULL, 512, "w+");
+ ATF_REQUIRE(fp != NULL);
+
+ /* fill the buffer */
+ for (i = 0; i < 512; i++) {
+ nofw = fwrite("a", 1, 1, fp);
+ ATF_REQUIRE(nofw == 1);
+ }
+
+ /* Get the current position into the stream. */
+ pos = ftell(fp);
+ ATF_REQUIRE(pos == 512);
+
+ /* Try to write past the end, we should get a short object count (0) */
+ nofw = fwrite("a", 1, 1, fp);
+ ATF_REQUIRE(nofw == 0);
+
+ /* Close the FILE *. */
+ rc = fclose(fp);
+ ATF_REQUIRE(rc == 0);
+
+ /* Open a FILE * using a wrong mode */
+ fp = fmemopen(NULL, 512, "r");
+ ATF_REQUIRE(fp == NULL);
+ ATF_REQUIRE(errno == EINVAL);
+
+ fp = fmemopen(NULL, 512, "w");
+ ATF_REQUIRE(fp == NULL);
+ ATF_REQUIRE(errno == EINVAL);
+}
+
+ATF_TC_WITHOUT_HEAD(test_data_length);
+ATF_TC_BODY(test_data_length, tc)
+{
+ /*
+ * Here we test that a read operation doesn't go past the end of the
+ * data actually written, and that a SEEK_END seeks from the end of the
+ * data, not of the whole buffer.
+ */
+ FILE *fp;
+ char buf[512] = {'\0'};
+ char str[] = "Test data length. ";
+ char str2[] = "Do we have two sentences?";
+ char str3[sizeof(str) + sizeof(str2) -1];
+ long pos;
+ size_t nofw, nofr;
+ int rc;
+
+ /* Open a FILE * for updating our buffer. */
+ fp = fmemopen(buf, sizeof(buf), "w+");
+ ATF_REQUIRE(fp != NULL);
+
+ /* Write our string into the buffer. */
+ nofw = fwrite(str, 1, sizeof(str), fp);
+ ATF_REQUIRE(nofw == sizeof(str));
+
+ /* Now seek to the end and check that ftell gives us sizeof(str). */
+ rc = fseek(fp, 0, SEEK_END);
+ ATF_REQUIRE(rc == 0);
+ pos = ftell(fp);
+ ATF_REQUIRE(pos == sizeof(str));
+
+ /* Close the FILE *. */
+ rc = fclose(fp);
+ ATF_REQUIRE(rc == 0);
+
+ /* Reopen the buffer for appending. */
+ fp = fmemopen(buf, sizeof(buf), "a+");
+ ATF_REQUIRE(fp != NULL);
+
+ /* We should now be writing after the first string. */
+ nofw = fwrite(str2, 1, sizeof(str2), fp);
+ ATF_REQUIRE(nofw == sizeof(str2));
+
+ /* Rewind the FILE *. */
+ rc = fseek(fp, 0, SEEK_SET);
+ ATF_REQUIRE(rc == 0);
+
+ /* Make sure we're at the beginning. */
+ pos = ftell(fp);
+ ATF_REQUIRE(pos == 0);
+
+ /* Read the whole buffer. */
+ nofr = fread(str3, 1, sizeof(buf), fp);
+ ATF_REQUIRE(nofr == sizeof(str3));
+
+ /* Make sure the two strings are there. */
+ ATF_REQUIRE(strncmp(str3, str, sizeof(str) - 1) == 0);
+ ATF_REQUIRE(strncmp(str3 + sizeof(str) - 1, str2, sizeof(str2)) == 0);
+
+ /* Close the FILE *. */
+ rc = fclose(fp);
+ ATF_REQUIRE(rc == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(test_binary);
+ATF_TC_BODY(test_binary, tc)
+{
+ /*
+ * Make sure that NULL bytes are never appended when opening a buffer
+ * in binary mode.
+ */
+
+ FILE *fp;
+ char buf[20];
+ char str[] = "Test";
+ size_t nofw;
+ int rc, i;
+
+ /* Pre-fill the buffer. */
+ memset(buf, 'A', sizeof(buf));
+
+ /* Open a FILE * in binary mode. */
+ fp = fmemopen(buf, sizeof(buf), "w+b");
+ ATF_REQUIRE(fp != NULL);
+
+ /* Write some data into it. */
+ nofw = fwrite(str, 1, strlen(str), fp);
+ ATF_REQUIRE(nofw == strlen(str));
+
+ /* Make sure that the buffer doesn't contain any NULL bytes. */
+ for (i = 0; i < sizeof(buf); i++)
+ ATF_REQUIRE(buf[i] != '\0');
+
+ /* Close the FILE *. */
+ rc = fclose(fp);
+ ATF_REQUIRE(rc == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(test_append_binary_pos);
+ATF_TC_BODY(test_append_binary_pos, tc)
+{
+ /*
+ * For compatibility with other implementations (glibc), we set the
+ * position to 0 when opening an automatically allocated binary stream
+ * for appending.
+ */
+
+ FILE *fp;
+
+ fp = fmemopen(NULL, 16, "ab+");
+ ATF_REQUIRE(fp != NULL);
+ ATF_REQUIRE(ftell(fp) == 0L);
+ fclose(fp);
+
+ /* Make sure that a pre-allocated buffer behaves correctly. */
+ char buf[] = "Hello";
+ fp = fmemopen(buf, sizeof(buf), "ab+");
+ ATF_REQUIRE(fp != NULL);
+ ATF_REQUIRE(ftell(fp) == strlen(buf));
+ fclose(fp);
+}
+
+ATF_TC_WITHOUT_HEAD(test_size_0);
+ATF_TC_BODY(test_size_0, tc)
+{
+ /* POSIX mandates that we return EINVAL if size is 0. */
+
+ FILE *fp;
+
+ fp = fmemopen(NULL, 0, "r+");
+ ATF_REQUIRE(fp == NULL);
+ ATF_REQUIRE(errno == EINVAL);
+}
+
+/*
+ * PR281953 - ensure we cannot write in read-only only mode, and cannot read in
+ * write-only mode.
+ */
+ATF_TC_WITHOUT_HEAD(test_rdonly_wronly);
+ATF_TC_BODY(test_rdonly_wronly, tc)
+{
+ FILE *fp;
+ char buf[16];
+ char buf_orig[16] = "input data";
+ char buf_write[16] = "write";
+ size_t sz;
+
+ memcpy(buf, buf_orig, sizeof(buf));
+ fp = fmemopen(buf, sizeof(buf), "r");
+ ATF_REQUIRE(fp != NULL);
+ sz = fwrite(buf_write, 1, strlen(buf_write), fp);
+ ATF_REQUIRE(sz == 0);
+ ATF_REQUIRE(errno == EBADF);
+ ATF_REQUIRE(memcmp(buf, buf_orig, sizeof(buf)) == 0);
+ fclose(fp);
+
+ fp = fmemopen(buf_orig, sizeof(buf), "w");
+ ATF_REQUIRE(fp != NULL);
+ sz = fread(buf, sizeof(buf), 1, fp);
+ ATF_REQUIRE(sz == 0);
+ ATF_REQUIRE(errno == EBADF);
+ fclose(fp);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, test_autoalloc);
+ ATF_TP_ADD_TC(tp, test_preexisting);
+ ATF_TP_ADD_TC(tp, test_data_length);
+ ATF_TP_ADD_TC(tp, test_binary);
+ ATF_TP_ADD_TC(tp, test_append_binary_pos);
+ ATF_TP_ADD_TC(tp, test_size_0);
+ ATF_TP_ADD_TC(tp, test_rdonly_wronly);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdio/fopen_test.c b/lib/libc/tests/stdio/fopen_test.c
new file mode 100644
index 000000000000..4dda5cbf0e5e
--- /dev/null
+++ b/lib/libc/tests/stdio/fopen_test.c
@@ -0,0 +1,202 @@
+/*-
+ * Copyright (c) 2013 Jilles Tjoelker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+/*
+ * O_ACCMODE is currently defined incorrectly. This is what it should be.
+ * Various code depends on the incorrect value.
+ */
+#define CORRECT_O_ACCMODE (O_ACCMODE | O_EXEC)
+
+static void
+runtest(const char *fname, const char *mode)
+{
+ FILE *fp;
+ int exp_fget_ret, fget_ret, fd, flags, wantedflags;
+
+ fp = fopen(fname, mode);
+ ATF_REQUIRE_MSG(fp != NULL,
+ "fopen(\"%s\", \"%s\") failed", fname, mode);
+ fd = fileno(fp);
+ ATF_REQUIRE_MSG(fd >= 0, "fileno() failed for fopen");
+ exp_fget_ret = strchr(mode, 'e') != NULL ? FD_CLOEXEC : 0;
+ ATF_REQUIRE_MSG((fget_ret = fcntl(fd, F_GETFD)) == exp_fget_ret,
+ "fcntl(.., F_GETFD) didn't FD_CLOEXEC as expected %d != %d",
+ exp_fget_ret, fget_ret);
+ flags = fcntl(fd, F_GETFL);
+ if (strchr(mode, '+'))
+ wantedflags = O_RDWR | (*mode == 'a' ? O_APPEND : 0);
+ else if (*mode == 'r')
+ wantedflags = O_RDONLY;
+ else if (*mode == 'w')
+ wantedflags = O_WRONLY;
+ else if (*mode == 'a')
+ wantedflags = O_WRONLY | O_APPEND;
+ else
+ wantedflags = -1;
+ fclose(fp);
+ if (wantedflags == -1)
+ atf_tc_fail("unrecognized mode: %s", mode);
+ else if ((flags & (CORRECT_O_ACCMODE | O_APPEND)) != wantedflags)
+ atf_tc_fail("incorrect access mode: %s", mode);
+}
+
+ATF_TC_WITHOUT_HEAD(fopen_r_test);
+ATF_TC_BODY(fopen_r_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "r");
+}
+
+ATF_TC_WITHOUT_HEAD(fopen_r_append_test);
+ATF_TC_BODY(fopen_r_append_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "r+");
+}
+
+ATF_TC_WITHOUT_HEAD(fopen_w_test);
+ATF_TC_BODY(fopen_w_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "w");
+}
+
+ATF_TC_WITHOUT_HEAD(fopen_w_append_test);
+ATF_TC_BODY(fopen_w_append_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "w+");
+}
+
+ATF_TC_WITHOUT_HEAD(fopen_a_test);
+ATF_TC_BODY(fopen_a_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "a");
+}
+
+ATF_TC_WITHOUT_HEAD(fopen_a_append_test);
+ATF_TC_BODY(fopen_a_append_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "a+");
+}
+
+ATF_TC_WITHOUT_HEAD(fopen_re_test);
+ATF_TC_BODY(fopen_re_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "re");
+}
+
+ATF_TC_WITHOUT_HEAD(fopen_r_append_e_test);
+ATF_TC_BODY(fopen_r_append_e_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "r+e");
+}
+
+ATF_TC_WITHOUT_HEAD(fopen_we_test);
+ATF_TC_BODY(fopen_we_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "we");
+}
+
+ATF_TC_WITHOUT_HEAD(fopen_w_append_e_test);
+ATF_TC_BODY(fopen_w_append_e_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "w+e");
+}
+
+ATF_TC_WITHOUT_HEAD(fopen_ae_test);
+ATF_TC_BODY(fopen_ae_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "ae");
+}
+
+ATF_TC_WITHOUT_HEAD(fopen_a_append_e_test);
+ATF_TC_BODY(fopen_a_append_e_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "a+e");
+}
+
+ATF_TC_WITHOUT_HEAD(fopen_re_append_test);
+ATF_TC_BODY(fopen_re_append_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "re+");
+}
+
+ATF_TC_WITHOUT_HEAD(fopen_we_append_test);
+ATF_TC_BODY(fopen_we_append_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "we+");
+}
+
+ATF_TC_WITHOUT_HEAD(fopen_ae_append_test);
+ATF_TC_BODY(fopen_ae_append_test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "ae+");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, fopen_r_test);
+ ATF_TP_ADD_TC(tp, fopen_r_append_test);
+ ATF_TP_ADD_TC(tp, fopen_w_test);
+ ATF_TP_ADD_TC(tp, fopen_w_append_test);
+ ATF_TP_ADD_TC(tp, fopen_a_test);
+ ATF_TP_ADD_TC(tp, fopen_a_append_test);
+ ATF_TP_ADD_TC(tp, fopen_re_test);
+ ATF_TP_ADD_TC(tp, fopen_r_append_e_test);
+ ATF_TP_ADD_TC(tp, fopen_we_test);
+ ATF_TP_ADD_TC(tp, fopen_w_append_e_test);
+ ATF_TP_ADD_TC(tp, fopen_ae_test);
+ ATF_TP_ADD_TC(tp, fopen_a_append_e_test);
+ ATF_TP_ADD_TC(tp, fopen_re_append_test);
+ ATF_TP_ADD_TC(tp, fopen_we_append_test);
+ ATF_TP_ADD_TC(tp, fopen_ae_append_test);
+
+ return (atf_no_error());
+}
+
+/*
+ vim:ts=8:cin:sw=8
+ */
diff --git a/lib/libc/tests/stdio/freopen_test.c b/lib/libc/tests/stdio/freopen_test.c
new file mode 100644
index 000000000000..55fe3c580dd3
--- /dev/null
+++ b/lib/libc/tests/stdio/freopen_test.c
@@ -0,0 +1,220 @@
+/*-
+ * Copyright (c) 2014 Jilles Tjoelker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <paths.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+static void
+runtest(const char *fname1, const char *mode1, const char *fname2,
+ const char *mode2, bool success)
+{
+ FILE *fp1, *fp2;
+ const char *fname2_print;
+
+ fname2_print = fname2 != NULL ? fname2 : "<NULL>";
+ fp1 = fopen(fname1, mode1);
+ ATF_REQUIRE_MSG(fp1 != NULL,
+ "fopen(\"%s\", \"%s\") failed; errno=%d", fname1, mode1, errno);
+ fp2 = freopen(fname2, mode2, fp1);
+ if (fp2 == NULL) {
+ ATF_REQUIRE_MSG(success == false,
+ "freopen(\"%s\", \"%s\", fopen(\"%s\", \"%s\")) succeeded "
+ "unexpectedly", fname2_print, mode2, fname1, mode1);
+ return;
+ }
+ ATF_REQUIRE_MSG(success == true,
+ "freopen(\"%s\", \"%s\", fopen(\"%s\", \"%s\")) failed: %d",
+ fname2_print, mode2, fname1, mode1, errno);
+ fclose(fp2);
+}
+
+ATF_TC_WITHOUT_HEAD(null__r__r__test);
+ATF_TC_BODY(null__r__r__test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "r", NULL, "r", true);
+}
+
+ATF_TC_WITHOUT_HEAD(null__w__r__test);
+ATF_TC_BODY(null__w__r__test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "w", NULL, "r", false);
+}
+
+ATF_TC_WITHOUT_HEAD(null__r_append__r__test);
+ATF_TC_BODY(null__r_append__r__test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "r+", NULL, "r", true);
+}
+
+ATF_TC_WITHOUT_HEAD(null__r__w__test);
+ATF_TC_BODY(null__r__w__test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "r", NULL, "w", false);
+}
+
+ATF_TC_WITHOUT_HEAD(null__w__w__test);
+ATF_TC_BODY(null__w__w__test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "w", NULL, "w", true);
+}
+
+ATF_TC_WITHOUT_HEAD(null__r_append__w__test);
+ATF_TC_BODY(null__r_append__w__test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "r+", NULL, "w", true);
+}
+
+ATF_TC_WITHOUT_HEAD(null__r__a__test);
+ATF_TC_BODY(null__r__a__test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "r", NULL, "a", false);
+}
+
+ATF_TC_WITHOUT_HEAD(null__w__a__test);
+ATF_TC_BODY(null__w__a__test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "w", NULL, "a", true);
+}
+
+ATF_TC_WITHOUT_HEAD(null__r_append__a__test);
+ATF_TC_BODY(null__r_append__a__test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "r+", NULL, "a", true);
+}
+
+ATF_TC_WITHOUT_HEAD(null__r__r_append__test);
+ATF_TC_BODY(null__r__r_append__test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "r", NULL, "r+", false);
+}
+
+ATF_TC_WITHOUT_HEAD(null__w__r_append__test);
+ATF_TC_BODY(null__w__r_append__test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "w", NULL, "r+", false);
+}
+
+ATF_TC_WITHOUT_HEAD(null__r_append__r_append__test);
+ATF_TC_BODY(null__r_append__r_append__test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "r+", NULL, "r+", true);
+}
+
+ATF_TC_WITHOUT_HEAD(null__r__w_append__test);
+ATF_TC_BODY(null__r__w_append__test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "r", NULL, "w+", false);
+}
+
+ATF_TC_WITHOUT_HEAD(null__w__w_append__test);
+ATF_TC_BODY(null__w__w_append__test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "w", NULL, "w+", false);
+}
+
+ATF_TC_WITHOUT_HEAD(null__r_append__w_append__test);
+ATF_TC_BODY(null__r_append__w_append__test, tc)
+{
+
+ runtest(_PATH_DEVNULL, "r+", NULL, "w+", true);
+}
+
+ATF_TC_WITHOUT_HEAD(sh__r__r__test);
+ATF_TC_BODY(sh__r__r__test, tc)
+{
+
+ runtest("/bin/sh", "r", NULL, "r", true);
+}
+
+ATF_TC_WITHOUT_HEAD(sh__sh__r__r__test);
+ATF_TC_BODY(sh__sh__r__r__test, tc)
+{
+
+ runtest("/bin/sh", "r", "/bin/sh", "r", true);
+}
+
+ATF_TC_WITHOUT_HEAD(sh__null__r__r__test);
+ATF_TC_BODY(sh__null__r__r__test, tc)
+{
+
+ runtest("/bin/sh", "r", _PATH_DEVNULL, "r", true);
+}
+
+ATF_TC_WITHOUT_HEAD(sh__null__r__w__test);
+ATF_TC_BODY(sh__null__r__w__test, tc)
+{
+
+ runtest("/bin/sh", "r", _PATH_DEVNULL, "w", true);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, null__r__r__test);
+ ATF_TP_ADD_TC(tp, null__w__r__test);
+ ATF_TP_ADD_TC(tp, null__r_append__r__test);
+ ATF_TP_ADD_TC(tp, null__r__w__test);
+ ATF_TP_ADD_TC(tp, null__w__w__test);
+ ATF_TP_ADD_TC(tp, null__r_append__w__test);
+ ATF_TP_ADD_TC(tp, null__r__a__test);
+ ATF_TP_ADD_TC(tp, null__w__a__test);
+ ATF_TP_ADD_TC(tp, null__r_append__a__test);
+ ATF_TP_ADD_TC(tp, null__r__r_append__test);
+ ATF_TP_ADD_TC(tp, null__w__r_append__test);
+ ATF_TP_ADD_TC(tp, null__r_append__r_append__test);
+ ATF_TP_ADD_TC(tp, null__r__w_append__test);
+ ATF_TP_ADD_TC(tp, null__w__w_append__test);
+ ATF_TP_ADD_TC(tp, null__r_append__w_append__test);
+ ATF_TP_ADD_TC(tp, sh__r__r__test);
+ ATF_TP_ADD_TC(tp, sh__sh__r__r__test);
+ ATF_TP_ADD_TC(tp, sh__null__r__r__test);
+ ATF_TP_ADD_TC(tp, sh__null__r__w__test);
+
+ return (atf_no_error());
+}
+
+/*
+ vim:ts=8:cin:sw=8
+ */
diff --git a/lib/libc/tests/stdio/getdelim_test.c b/lib/libc/tests/stdio/getdelim_test.c
new file mode 100644
index 000000000000..01a0ab0949fe
--- /dev/null
+++ b/lib/libc/tests/stdio/getdelim_test.c
@@ -0,0 +1,431 @@
+/*-
+ * Copyright (c) 2009 David Schultz <das@FreeBSD.org>
+ * Copyright (c) 2021 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:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <fcntl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#define CHUNK_MAX 10
+
+/* The assertions depend on this string. */
+char apothegm[] = "All work and no play\0 makes Jack a dull boy.\n";
+
+/*
+ * This is a neurotic reader function designed to give getdelim() a
+ * hard time. It reads through the string `apothegm' and returns a
+ * random number of bytes up to the requested length.
+ */
+static int
+_reader(void *cookie, char *buf, int len)
+{
+ size_t *offp = cookie;
+ size_t r;
+
+ r = random() % CHUNK_MAX + 1;
+ if (len > r)
+ len = r;
+ if (len > sizeof(apothegm) - *offp)
+ len = sizeof(apothegm) - *offp;
+ memcpy(buf, apothegm + *offp, len);
+ *offp += len;
+ return (len);
+}
+
+static FILE *
+mkfilebuf(void)
+{
+ size_t *offp;
+
+ offp = malloc(sizeof(*offp)); /* XXX leak */
+ *offp = 0;
+ return (fropen(offp, _reader));
+}
+
+ATF_TC_WITHOUT_HEAD(getline_basic);
+ATF_TC_BODY(getline_basic, tc)
+{
+ FILE *fp;
+ char *line;
+ size_t linecap;
+ int i;
+
+ srandom(0);
+
+ /*
+ * Test multiple times with different buffer sizes
+ * and different _reader() return values.
+ */
+ errno = 0;
+ for (i = 0; i < 8; i++) {
+ fp = mkfilebuf();
+ linecap = i;
+ line = malloc(i);
+ /* First line: the full apothegm */
+ ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1);
+ ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0);
+ ATF_REQUIRE(linecap >= sizeof(apothegm));
+ /* Second line: the NUL terminator following the newline */
+ ATF_REQUIRE(getline(&line, &linecap, fp) == 1);
+ ATF_REQUIRE(line[0] == '\0' && line[1] == '\0');
+ /* Third line: EOF */
+ line[0] = 'X';
+ ATF_REQUIRE(getline(&line, &linecap, fp) == -1);
+ ATF_REQUIRE(line[0] == '\0');
+ free(line);
+ line = NULL;
+ ATF_REQUIRE(feof(fp));
+ ATF_REQUIRE(!ferror(fp));
+ fclose(fp);
+ }
+ ATF_REQUIRE(errno == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(stream_error);
+ATF_TC_BODY(stream_error, tc)
+{
+ char *line;
+ size_t linecap;
+
+ /* Make sure read errors are handled properly. */
+ line = NULL;
+ linecap = 0;
+ errno = 0;
+ ATF_REQUIRE(getline(&line, &linecap, stdout) == -1);
+ ATF_REQUIRE(errno == EBADF);
+ errno = 0;
+ ATF_REQUIRE(getdelim(&line, &linecap, 'X', stdout) == -1);
+ ATF_REQUIRE(errno == EBADF);
+ ATF_REQUIRE(ferror(stdout));
+}
+
+ATF_TC_WITHOUT_HEAD(invalid_params);
+ATF_TC_BODY(invalid_params, tc)
+{
+ FILE *fp;
+ char *line;
+ size_t linecap;
+
+ /* Make sure NULL linep or linecapp pointers are handled. */
+ fp = mkfilebuf();
+ ATF_REQUIRE(getline(NULL, &linecap, fp) == -1);
+ ATF_REQUIRE(errno == EINVAL);
+ ATF_REQUIRE(getline(&line, NULL, fp) == -1);
+ ATF_REQUIRE(errno == EINVAL);
+ ATF_REQUIRE(ferror(fp));
+ fclose(fp);
+}
+
+ATF_TC_WITHOUT_HEAD(eof);
+ATF_TC_BODY(eof, tc)
+{
+ FILE *fp;
+ char *line;
+ size_t linecap;
+
+ /* Make sure getline() allocates memory as needed if fp is at EOF. */
+ errno = 0;
+ fp = mkfilebuf();
+ while (!feof(fp)) /* advance to EOF; can't fseek this stream */
+ getc(fp);
+ line = NULL;
+ linecap = 0;
+ printf("getline\n");
+ ATF_REQUIRE(getline(&line, &linecap, fp) == -1);
+ ATF_REQUIRE(line[0] == '\0');
+ ATF_REQUIRE(linecap > 0);
+ ATF_REQUIRE(errno == 0);
+ printf("feof\n");
+ ATF_REQUIRE(feof(fp));
+ ATF_REQUIRE(!ferror(fp));
+ fclose(fp);
+}
+
+ATF_TC_WITHOUT_HEAD(nul);
+ATF_TC_BODY(nul, tc)
+{
+ FILE *fp;
+ char *line;
+ size_t linecap, n;
+
+ errno = 0;
+ line = NULL;
+ linecap = 0;
+ /* Make sure a NUL delimiter works. */
+ fp = mkfilebuf();
+ n = strlen(apothegm);
+ printf("getdelim\n");
+ ATF_REQUIRE(getdelim(&line, &linecap, '\0', fp) == n + 1);
+ ATF_REQUIRE(strcmp(line, apothegm) == 0);
+ ATF_REQUIRE(line[n + 1] == '\0');
+ ATF_REQUIRE(linecap > n + 1);
+ n = strlen(apothegm + n + 1);
+ printf("getdelim 2\n");
+ ATF_REQUIRE(getdelim(&line, &linecap, '\0', fp) == n + 1);
+ ATF_REQUIRE(line[n + 1] == '\0');
+ ATF_REQUIRE(linecap > n + 1);
+ ATF_REQUIRE(errno == 0);
+ ATF_REQUIRE(!ferror(fp));
+ fclose(fp);
+}
+
+ATF_TC_WITHOUT_HEAD(empty_NULL_buffer);
+ATF_TC_BODY(empty_NULL_buffer, tc)
+{
+ FILE *fp;
+ char *line;
+ size_t linecap;
+
+ /* Make sure NULL *linep and zero *linecapp are handled. */
+ fp = mkfilebuf();
+ line = NULL;
+ linecap = 42;
+ ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1);
+ ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0);
+ fp = mkfilebuf();
+ free(line);
+ line = malloc(100);
+ linecap = 0;
+ ATF_REQUIRE(getline(&line, &linecap, fp) == sizeof(apothegm) - 1);
+ ATF_REQUIRE(memcmp(line, apothegm, sizeof(apothegm)) == 0);
+ free(line);
+ ATF_REQUIRE(!ferror(fp));
+ fclose(fp);
+}
+
+static void
+_ipc_read(int fd, char wait_c)
+{
+ char c;
+ ssize_t len;
+
+ c = 0;
+ while (c != wait_c) {
+ len = read(fd, &c, 1);
+ ATF_CHECK_MSG(len != 0,
+ "EOF on IPC pipe while waiting. Did other side fail?");
+ ATF_CHECK_MSG(len == 1 || errno == EINTR,
+ "read %zu bytes errno %d\n", len, errno);
+ if (len != 1 || errno != EINTR)
+ break;
+ }
+}
+
+static void
+_ipc_write(int fd, char c)
+{
+
+ while ((write(fd, &c, 1) != 1))
+ ATF_REQUIRE(errno == EINTR);
+}
+
+static void
+ipc_wait(int ipcfd[2])
+{
+
+ _ipc_read(ipcfd[0], '+');
+ /* Send ACK. */
+ _ipc_write(ipcfd[1], '-');
+}
+
+static void
+ipc_wakeup(int ipcfd[2])
+{
+
+ _ipc_write(ipcfd[1], '+');
+ /* Wait for ACK. */
+ _ipc_read(ipcfd[0], '-');
+}
+
+static void
+_nonblock_eagain(int buf_mode)
+{
+ FILE *fp;
+ const char delim = '!';
+ const char *strs[] = {
+ "first line partial!",
+ "second line is sent in full!",
+ "third line is sent partially!",
+ "last line is sent in full!",
+ };
+ char *line;
+ size_t linecap, strslen[nitems(strs)];
+ ssize_t linelen;
+ int fd_fifo, flags, i, ipcfd[2], pipedes[2], pipedes2[2], status;
+ pid_t pid;
+
+ line = NULL;
+ linecap = 0;
+ for (i = 0; i < nitems(strslen); i++)
+ strslen[i] = strlen(strs[i]);
+ ATF_REQUIRE(pipe2(pipedes, O_CLOEXEC) == 0);
+ ATF_REQUIRE(pipe2(pipedes2, O_CLOEXEC) == 0);
+
+ (void)unlink("fifo");
+ ATF_REQUIRE(mkfifo("fifo", 0666) == 0);
+ ATF_REQUIRE((pid = fork()) >= 0);
+ if (pid == 0) {
+ close(pipedes[0]);
+ ipcfd[1] = pipedes[1];
+ ipcfd[0] = pipedes2[0];
+ close(pipedes2[1]);
+
+ ATF_REQUIRE((fd_fifo = open("fifo", O_WRONLY)) != -1);
+
+ /* Partial write. */
+ ATF_REQUIRE(write(fd_fifo, strs[0], strslen[0] - 3) ==
+ strslen[0] - 3);
+ ipc_wakeup(ipcfd);
+
+ ipc_wait(ipcfd);
+ /* Finish off the first line. */
+ ATF_REQUIRE(write(fd_fifo,
+ &(strs[0][strslen[0] - 3]), 3) == 3);
+ /* And include the second full line and a partial 3rd line. */
+ ATF_REQUIRE(write(fd_fifo, strs[1], strslen[1]) == strslen[1]);
+ ATF_REQUIRE(write(fd_fifo, strs[2], strslen[2] - 3) ==
+ strslen[2] - 3);
+ ipc_wakeup(ipcfd);
+
+ ipc_wait(ipcfd);
+ /* Finish the partial write and partially send the last. */
+ ATF_REQUIRE(write(fd_fifo,
+ &(strs[2][strslen[2] - 3]), 3) == 3);
+ ATF_REQUIRE(write(fd_fifo, strs[3], strslen[3] - 3) ==
+ strslen[3] - 3);
+ ipc_wakeup(ipcfd);
+
+ ipc_wait(ipcfd);
+ /* Finish the write */
+ ATF_REQUIRE(write(fd_fifo,
+ &(strs[3][strslen[3] - 3]), 3) == 3);
+ ipc_wakeup(ipcfd);
+ _exit(0);
+ }
+ ipcfd[0] = pipedes[0];
+ close(pipedes[1]);
+ close(pipedes2[0]);
+ ipcfd[1] = pipedes2[1];
+
+ ATF_REQUIRE((fp = fopen("fifo", "r")) != NULL);
+ setvbuf(fp, (char *)NULL, buf_mode, 0);
+ ATF_REQUIRE((flags = fcntl(fileno(fp), F_GETFL, 0)) != -1);
+ ATF_REQUIRE(fcntl(fileno(fp), F_SETFL, flags | O_NONBLOCK) >= 0);
+
+ /* Wait until the writer completes its partial write. */
+ ipc_wait(ipcfd);
+ ATF_REQUIRE_ERRNO(EAGAIN,
+ (linelen = getdelim(&line, &linecap, delim, fp)) == -1);
+ ATF_REQUIRE_STREQ("", line);
+ ATF_REQUIRE(ferror(fp));
+ ATF_REQUIRE(!feof(fp));
+ clearerr(fp);
+ ipc_wakeup(ipcfd);
+
+ ipc_wait(ipcfd);
+ /*
+ * Should now have the finished first line, a full second line,
+ * and a partial third line.
+ */
+ ATF_CHECK(getdelim(&line, &linecap, delim, fp) == strslen[0]);
+ ATF_REQUIRE_STREQ(strs[0], line);
+ ATF_REQUIRE(getdelim(&line, &linecap, delim, fp) == strslen[1]);
+ ATF_REQUIRE_STREQ(strs[1], line);
+
+ ATF_REQUIRE_ERRNO(EAGAIN,
+ (linelen = getdelim(&line, &linecap, delim, fp)) == -1);
+ ATF_REQUIRE_STREQ("", line);
+ ATF_REQUIRE(ferror(fp));
+ ATF_REQUIRE(!feof(fp));
+ clearerr(fp);
+ ipc_wakeup(ipcfd);
+
+ /* Wait for the partial write to be completed and another to be done. */
+ ipc_wait(ipcfd);
+ ATF_REQUIRE((linelen = getdelim(&line, &linecap, delim, fp)) != -1);
+ ATF_REQUIRE(!ferror(fp));
+ ATF_REQUIRE(!feof(fp));
+ ATF_REQUIRE_STREQ(strs[2], line);
+ ATF_REQUIRE(linelen == strslen[2]);
+
+ ATF_REQUIRE_ERRNO(EAGAIN,
+ (linelen = getdelim(&line, &linecap, delim, fp)) == -1);
+ ATF_REQUIRE_STREQ("", line);
+ ATF_REQUIRE(ferror(fp));
+ ATF_REQUIRE(!feof(fp));
+ clearerr(fp);
+ ipc_wakeup(ipcfd);
+
+ ipc_wait(ipcfd);
+ ATF_REQUIRE((linelen = getdelim(&line, &linecap, delim, fp)) != -1);
+ ATF_REQUIRE(!ferror(fp));
+ ATF_REQUIRE(!feof(fp));
+ ATF_REQUIRE_STREQ(strs[3], line);
+ ATF_REQUIRE(linelen == strslen[3]);
+
+ ATF_REQUIRE(waitpid(pid, &status, WEXITED) != -1);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE(WEXITSTATUS(status) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(nonblock_eagain_buffered);
+ATF_TC_BODY(nonblock_eagain_buffered, tc)
+{
+
+ _nonblock_eagain(_IOFBF);
+}
+
+ATF_TC_WITHOUT_HEAD(nonblock_eagain_unbuffered);
+ATF_TC_BODY(nonblock_eagain_unbuffered, tc)
+{
+
+ _nonblock_eagain(_IONBF);
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, getline_basic);
+ ATF_TP_ADD_TC(tp, stream_error);
+ ATF_TP_ADD_TC(tp, eof);
+ ATF_TP_ADD_TC(tp, invalid_params);
+ ATF_TP_ADD_TC(tp, nul);
+ ATF_TP_ADD_TC(tp, empty_NULL_buffer);
+ ATF_TP_ADD_TC(tp, nonblock_eagain_unbuffered);
+ ATF_TP_ADD_TC(tp, nonblock_eagain_buffered);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdio/gets_s_test.c b/lib/libc/tests/stdio/gets_s_test.c
new file mode 100644
index 000000000000..4e1a59006b60
--- /dev/null
+++ b/lib/libc/tests/stdio/gets_s_test.c
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 2017 Cyril S. E. Schubert
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+
+static errno_t error_code;
+static const char * message;
+
+void
+h(const char * msg, void * ptr __unused, errno_t error)
+{
+ error_code = error;
+ message = msg;
+}
+
+/* null ptr */
+ATF_TC_WITHOUT_HEAD(null_ptr);
+ATF_TC_BODY(null_ptr, tc)
+{
+ ATF_CHECK_MSG(gets_s(NULL, 1) == NULL,
+ "gets_s() failed to handle NULL pointer");
+}
+
+/* normal */
+ATF_TC_WITHOUT_HEAD(normal);
+ATF_TC_BODY(normal, tc)
+{
+ pid_t kidpid;
+ int fd[2];
+ int nfd;
+
+ // close(STDIN_FILENO);
+ // close(STDOUT_FILENO);
+ pipe(fd);
+
+ if ((kidpid = fork()) == 0) {
+ char b[10];
+
+ close(fd[1]);
+ nfd = dup2(fd[0], 0);
+ close(fd[0]);
+ stdin = fdopen(nfd, "r");
+ ATF_CHECK_MSG(gets_s(b, sizeof(b)) == 0, "gets_s() normal failed");
+ fclose(stdin);
+ } else {
+ int stat;
+
+ close(fd[0]);
+ stdout = fdopen(fd[1], "w");
+ puts("a sting");
+ fclose(stdout);
+ (void) waitpid(kidpid, &stat, WEXITED);
+ }
+}
+
+/* n > rmax */
+ATF_TC_WITHOUT_HEAD(n_gt_rmax);
+ATF_TC_BODY(n_gt_rmax, tc)
+{
+ char b;
+
+ ATF_CHECK_MSG(gets_s(&b, RSIZE_MAX + 1) == NULL,
+ "gets_s() n > RSIZE_MAX");
+}
+
+/* n == 0 */
+ATF_TC_WITHOUT_HEAD(n_eq_zero);
+ATF_TC_BODY(n_eq_zero, tc)
+{
+ char b;
+
+ ATF_CHECK_MSG(gets_s(&b, 0) == NULL, "gets_s() n is zero");
+}
+
+/* n > rmax, handler */
+ATF_TC_WITHOUT_HEAD(n_gt_rmax_handler);
+ATF_TC_BODY(n_gt_rmax_handler, tc)
+{
+ char b;
+
+ error_code = 0;
+ message = NULL;
+ set_constraint_handler_s(h);
+ ATF_CHECK_MSG(gets_s(&b, RSIZE_MAX + 1) == NULL, "gets_s() n > RSIZE_MAX");
+ ATF_CHECK_MSG(error_code > 0, "gets_s() error code is %d", error_code);
+ ATF_CHECK_MSG(strcmp(message, "gets_s : n > RSIZE_MAX") == 0, "gets_s(): incorrect error message");
+}
+
+/* n == 0, handler */
+ATF_TC_WITHOUT_HEAD(n_eq_zero_handler);
+ATF_TC_BODY(n_eq_zero_handler, tc)
+{
+ char b;
+
+ error_code = 0;
+ message = NULL;
+ set_constraint_handler_s(h);
+ ATF_CHECK(gets_s(&b, 0) == NULL);
+ ATF_CHECK_MSG(error_code > 0, "gets_s() error code is %d", error_code);
+ ATF_CHECK_MSG(strcmp(message, "gets_s : n == 0") == 0, "gets_s(): incorrect error message");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, null_ptr);
+ ATF_TP_ADD_TC(tp, normal);
+ ATF_TP_ADD_TC(tp, n_gt_rmax);
+ ATF_TP_ADD_TC(tp, n_eq_zero);
+ ATF_TP_ADD_TC(tp, n_gt_rmax_handler);
+ ATF_TP_ADD_TC(tp, n_eq_zero_handler);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdio/mkostemp_test.c b/lib/libc/tests/stdio/mkostemp_test.c
new file mode 100644
index 000000000000..b31335fed43f
--- /dev/null
+++ b/lib/libc/tests/stdio/mkostemp_test.c
@@ -0,0 +1,182 @@
+/*-
+ * Copyright (c) 2013 Jilles Tjoelker
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test program for mkostemp().
+ */
+
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+static const char template[] = "mkostemp.XXXXXXXX";
+static int testnum;
+
+#define MISCFLAGS (O_APPEND | O_DIRECT | O_SHLOCK | O_EXLOCK | O_SYNC)
+
+static void
+test_one(int oflags)
+{
+ char tmpf[sizeof(template)];
+ struct stat st1, st2;
+ int fd;
+
+ memcpy(tmpf, template, sizeof(tmpf));
+ fd = mkostemp(tmpf, oflags);
+ if (fd < 0) {
+ printf("not ok %d - oflags=%#x "
+ "mkostemp() reported failure: %s\n",
+ testnum++, oflags, strerror(errno));
+ return;
+ }
+ if (memcmp(tmpf, template, sizeof(tmpf) - 8 - 1) != 0) {
+ printf("not ok %d - oflags=%#x "
+ "returned pathname does not match template: %s\n",
+ testnum++, oflags, tmpf);
+ return;
+ }
+ do {
+ if (fcntl(fd, F_GETFD) !=
+ (oflags & O_CLOEXEC ? FD_CLOEXEC : 0)) {
+ printf("not ok %d - oflags=%#x "
+ "close-on-exec flag incorrect\n",
+ testnum++, oflags);
+ break;
+ }
+ if ((fcntl(fd, F_GETFL) & MISCFLAGS) != (oflags & MISCFLAGS)) {
+ printf("not ok %d - oflags=%#x "
+ "open flags incorrect\n",
+ testnum++, oflags);
+ break;
+ }
+ if (stat(tmpf, &st1) == -1) {
+ printf("not ok %d - oflags=%#x "
+ "cannot stat returned pathname %s: %s\n",
+ testnum++, oflags, tmpf, strerror(errno));
+ break;
+ }
+ if (fstat(fd, &st2) == -1) {
+ printf("not ok %d - oflags=%#x "
+ "cannot fstat returned fd %d: %s\n",
+ testnum++, oflags, fd, strerror(errno));
+ break;
+ }
+ if (!S_ISREG(st1.st_mode) || (st1.st_mode & 0777) != 0600 ||
+ st1.st_nlink != 1 || st1.st_size != 0) {
+ printf("not ok %d - oflags=%#x "
+ "named file attributes incorrect\n",
+ testnum++, oflags);
+ break;
+ }
+ if (!S_ISREG(st2.st_mode) || (st2.st_mode & 0777) != 0600 ||
+ st2.st_nlink != 1 || st2.st_size != 0) {
+ printf("not ok %d - oflags=%#x "
+ "opened file attributes incorrect\n",
+ testnum++, oflags);
+ break;
+ }
+ if (st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino) {
+ printf("not ok %d - oflags=%#x "
+ "named and opened file do not match\n",
+ testnum++, oflags);
+ break;
+ }
+ (void)unlink(tmpf);
+ if (fstat(fd, &st2) == -1)
+ printf("not ok %d - oflags=%#x "
+ "cannot fstat returned fd %d again: %s\n",
+ testnum++, oflags, fd, strerror(errno));
+ else if (st2.st_nlink != 0)
+ printf("not ok %d - oflags=%#x "
+ "st_nlink is not 0 after unlink\n",
+ testnum++, oflags);
+ else
+ printf("ok %d - oflags=%#x\n", testnum++, oflags);
+ (void)close(fd);
+ return;
+ } while (0);
+ (void)close(fd);
+ (void)unlink(tmpf);
+}
+
+ATF_TC_WITHOUT_HEAD(zero);
+ATF_TC_BODY(zero, tc)
+{
+
+ test_one(0);
+}
+
+ATF_TC_WITHOUT_HEAD(O_CLOEXEC);
+ATF_TC_BODY(O_CLOEXEC, tc)
+{
+
+ test_one(O_CLOEXEC);
+}
+
+ATF_TC_WITHOUT_HEAD(O_APPEND);
+ATF_TC_BODY(O_APPEND, tc)
+{
+
+ test_one(O_APPEND);
+}
+
+ATF_TC_WITHOUT_HEAD(O_APPEND__O_CLOEXEC);
+ATF_TC_BODY(O_APPEND__O_CLOEXEC, tc)
+{
+
+ test_one(O_APPEND|O_CLOEXEC);
+}
+
+ATF_TC_WITHOUT_HEAD(bad_flags);
+ATF_TC_BODY(bad_flags, tc)
+{
+
+ char tmpf[sizeof(template)];
+
+ memcpy(tmpf, template, sizeof(tmpf));
+ ATF_REQUIRE_MSG(mkostemp(tmpf, O_CREAT) == -1,
+ "mkostemp(O_CREAT) succeeded unexpectedly");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, zero);
+ ATF_TP_ADD_TC(tp, O_CLOEXEC);
+ ATF_TP_ADD_TC(tp, O_APPEND);
+ ATF_TP_ADD_TC(tp, O_APPEND__O_CLOEXEC);
+ ATF_TP_ADD_TC(tp, bad_flags);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdio/open_memstream2_test.c b/lib/libc/tests/stdio/open_memstream2_test.c
new file mode 100644
index 000000000000..c9c6528832d8
--- /dev/null
+++ b/lib/libc/tests/stdio/open_memstream2_test.c
@@ -0,0 +1,198 @@
+/*-
+ * Copyright (c) 2013 Hudson River Trading LLC
+ * Written by: John H. Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <atf-c.h>
+
+static char *buf;
+static size_t len;
+
+static void
+assert_stream(const char *contents)
+{
+ if (strlen(contents) != len)
+ printf("bad length %zd for \"%s\"\n", len, contents);
+ else if (strncmp(buf, contents, strlen(contents)) != 0)
+ printf("bad buffer \"%s\" for \"%s\"\n", buf, contents);
+}
+
+ATF_TC_WITHOUT_HEAD(open_group_test);
+ATF_TC_BODY(open_group_test, tc)
+{
+ FILE *fp;
+ off_t eob;
+
+ fp = open_memstream(&buf, &len);
+ ATF_REQUIRE_MSG(fp != NULL, "open_memstream failed");
+
+ fprintf(fp, "hello my world");
+ fflush(fp);
+ assert_stream("hello my world");
+ eob = ftello(fp);
+ rewind(fp);
+ fprintf(fp, "good-bye");
+ fseeko(fp, eob, SEEK_SET);
+ fclose(fp);
+ assert_stream("good-bye world");
+ free(buf);
+}
+
+ATF_TC_WITHOUT_HEAD(simple_tests);
+ATF_TC_BODY(simple_tests, tc)
+{
+ static const char zerobuf[] =
+ { 'f', 'o', 'o', 0, 0, 0, 0, 'b', 'a', 'r', 0 };
+ char c;
+ FILE *fp;
+
+ fp = open_memstream(&buf, NULL);
+ ATF_REQUIRE_MSG(fp == NULL, "open_memstream did not fail");
+ ATF_REQUIRE_MSG(errno == EINVAL,
+ "open_memstream didn't fail with EINVAL");
+ fp = open_memstream(NULL, &len);
+ ATF_REQUIRE_MSG(fp == NULL, "open_memstream did not fail");
+ ATF_REQUIRE_MSG(errno == EINVAL,
+ "open_memstream didn't fail with EINVAL");
+ fp = open_memstream(&buf, &len);
+ ATF_REQUIRE_MSG(fp != NULL, "open_memstream failed; errno=%d", errno);
+ fflush(fp);
+ assert_stream("");
+ if (fwide(fp, 0) >= 0)
+ printf("stream is not byte-oriented\n");
+
+ fprintf(fp, "fo");
+ fflush(fp);
+ assert_stream("fo");
+ fputc('o', fp);
+ fflush(fp);
+ assert_stream("foo");
+ rewind(fp);
+ fflush(fp);
+ assert_stream("");
+ fseek(fp, 0, SEEK_END);
+ fflush(fp);
+ assert_stream("foo");
+
+ /*
+ * Test seeking out past the current end. Should zero-fill the
+ * intermediate area.
+ */
+ fseek(fp, 4, SEEK_END);
+ fprintf(fp, "bar");
+ fflush(fp);
+
+ /*
+ * Can't use assert_stream() here since this should contain
+ * embedded null characters.
+ */
+ if (len != 10)
+ printf("bad length %zd for zero-fill test\n", len);
+ else if (memcmp(buf, zerobuf, sizeof(zerobuf)) != 0)
+ printf("bad buffer for zero-fill test\n");
+
+ fseek(fp, 3, SEEK_SET);
+ fprintf(fp, " in ");
+ fflush(fp);
+ assert_stream("foo in ");
+ fseek(fp, 0, SEEK_END);
+ fflush(fp);
+ assert_stream("foo in bar");
+
+ rewind(fp);
+ if (fread(&c, sizeof(c), 1, fp) != 0)
+ printf("fread did not fail\n");
+ else if (!ferror(fp))
+ printf("error indicator not set after fread\n");
+ else
+ clearerr(fp);
+
+ fseek(fp, 4, SEEK_SET);
+ fprintf(fp, "bar baz");
+ fclose(fp);
+ assert_stream("foo bar baz");
+ free(buf);
+}
+
+ATF_TC_WITHOUT_HEAD(seek_tests);
+ATF_TC_BODY(seek_tests, tc)
+{
+ FILE *fp;
+
+ fp = open_memstream(&buf, &len);
+ ATF_REQUIRE_MSG(fp != NULL, "open_memstream failed: %d", errno);
+
+#define SEEK_FAIL(offset, whence, error) do { \
+ errno = 0; \
+ ATF_REQUIRE_MSG(fseeko(fp, (offset), (whence)) != 0, \
+ "fseeko(%s, %s) did not fail, set pos to %jd", \
+ __STRING(offset), __STRING(whence), \
+ (intmax_t)ftello(fp)); \
+ ATF_REQUIRE_MSG(errno == (error), \
+ "fseeko(%s, %s) failed with %d rather than %s", \
+ __STRING(offset), __STRING(whence), errno, \
+ __STRING(error)); \
+} while (0)
+
+#define SEEK_OK(offset, whence, result) do { \
+ ATF_REQUIRE_MSG(fseeko(fp, (offset), (whence)) == 0, \
+ "fseeko(%s, %s) failed: %s", \
+ __STRING(offset), __STRING(whence), strerror(errno)); \
+ ATF_REQUIRE_MSG(ftello(fp) == (result), \
+ "fseeko(%s, %s) seeked to %jd rather than %s", \
+ __STRING(offset), __STRING(whence), \
+ (intmax_t)ftello(fp), __STRING(result)); \
+} while (0)
+
+ SEEK_FAIL(-1, SEEK_SET, EINVAL);
+ SEEK_FAIL(-1, SEEK_CUR, EINVAL);
+ SEEK_FAIL(-1, SEEK_END, EINVAL);
+ fprintf(fp, "foo");
+ SEEK_OK(-1, SEEK_CUR, 2);
+ SEEK_OK(0, SEEK_SET, 0);
+ SEEK_OK(-1, SEEK_END, 2);
+ SEEK_OK(OFF_MAX - 1, SEEK_SET, OFF_MAX - 1);
+ SEEK_FAIL(2, SEEK_CUR, EOVERFLOW);
+ fclose(fp);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, open_group_test);
+ ATF_TP_ADD_TC(tp, simple_tests);
+ ATF_TP_ADD_TC(tp, seek_tests);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdio/open_wmemstream_test.c b/lib/libc/tests/stdio/open_wmemstream_test.c
new file mode 100644
index 000000000000..7ab882a4740a
--- /dev/null
+++ b/lib/libc/tests/stdio/open_wmemstream_test.c
@@ -0,0 +1,198 @@
+/*-
+ * Copyright (c) 2013 Hudson River Trading LLC
+ * Written by: John H. Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <atf-c.h>
+
+static wchar_t *buf;
+static size_t len;
+
+static void
+assert_stream(const wchar_t *contents)
+{
+ if (wcslen(contents) != len)
+ printf("bad length %zd for \"%ls\"\n", len, contents);
+ else if (wcsncmp(buf, contents, wcslen(contents)) != 0)
+ printf("bad buffer \"%ls\" for \"%ls\"\n", buf, contents);
+}
+
+ATF_TC_WITHOUT_HEAD(open_group_test);
+ATF_TC_BODY(open_group_test, tc)
+{
+ FILE *fp;
+ off_t eob;
+
+ fp = open_wmemstream(&buf, &len);
+ ATF_REQUIRE_MSG(fp != NULL, "open_wmemstream failed");
+
+ fwprintf(fp, L"hello my world");
+ fflush(fp);
+ assert_stream(L"hello my world");
+ eob = ftello(fp);
+ rewind(fp);
+ fwprintf(fp, L"good-bye");
+ fseeko(fp, eob, SEEK_SET);
+ fclose(fp);
+ assert_stream(L"good-bye world");
+ free(buf);
+}
+
+ATF_TC_WITHOUT_HEAD(simple_tests);
+ATF_TC_BODY(simple_tests, tc)
+{
+ static const wchar_t zerobuf[] =
+ { L'f', L'o', L'o', 0, 0, 0, 0, L'b', L'a', L'r', 0 };
+ wchar_t c;
+ FILE *fp;
+
+ fp = open_wmemstream(&buf, NULL);
+ ATF_REQUIRE_MSG(fp == NULL, "open_wmemstream did not fail");
+ ATF_REQUIRE_MSG(errno == EINVAL,
+ "open_wmemstream didn't fail with EINVAL");
+ fp = open_wmemstream(NULL, &len);
+ ATF_REQUIRE_MSG(fp == NULL, "open_wmemstream did not fail");
+ ATF_REQUIRE_MSG(errno == EINVAL,
+ "open_wmemstream didn't fail with EINVAL");
+ fp = open_wmemstream(&buf, &len);
+ ATF_REQUIRE_MSG(fp != NULL, "open_memstream failed; errno=%d", errno);
+ fflush(fp);
+ assert_stream(L"");
+ if (fwide(fp, 0) <= 0)
+ printf("stream is not wide-oriented\n");
+
+ fwprintf(fp, L"fo");
+ fflush(fp);
+ assert_stream(L"fo");
+ fputwc(L'o', fp);
+ fflush(fp);
+ assert_stream(L"foo");
+ rewind(fp);
+ fflush(fp);
+ assert_stream(L"");
+ fseek(fp, 0, SEEK_END);
+ fflush(fp);
+ assert_stream(L"foo");
+
+ /*
+ * Test seeking out past the current end. Should zero-fill the
+ * intermediate area.
+ */
+ fseek(fp, 4, SEEK_END);
+ fwprintf(fp, L"bar");
+ fflush(fp);
+
+ /*
+ * Can't use assert_stream() here since this should contain
+ * embedded null characters.
+ */
+ if (len != 10)
+ printf("bad length %zd for zero-fill test\n", len);
+ else if (memcmp(buf, zerobuf, sizeof(zerobuf)) != 0)
+ printf("bad buffer for zero-fill test\n");
+
+ fseek(fp, 3, SEEK_SET);
+ fwprintf(fp, L" in ");
+ fflush(fp);
+ assert_stream(L"foo in ");
+ fseek(fp, 0, SEEK_END);
+ fflush(fp);
+ assert_stream(L"foo in bar");
+
+ rewind(fp);
+ if (fread(&c, sizeof(c), 1, fp) != 0)
+ printf("fread did not fail\n");
+ else if (!ferror(fp))
+ printf("error indicator not set after fread\n");
+ else
+ clearerr(fp);
+
+ fseek(fp, 4, SEEK_SET);
+ fwprintf(fp, L"bar baz");
+ fclose(fp);
+ assert_stream(L"foo bar baz");
+ free(buf);
+}
+
+ATF_TC_WITHOUT_HEAD(seek_tests);
+ATF_TC_BODY(seek_tests, tc)
+{
+ FILE *fp;
+
+ fp = open_wmemstream(&buf, &len);
+ ATF_REQUIRE_MSG(fp != NULL, "open_wmemstream failed; errno=%d", errno);
+
+#define SEEK_FAIL(offset, whence, error) do { \
+ errno = 0; \
+ ATF_REQUIRE_MSG(fseeko(fp, (offset), (whence)) != 0, \
+ "fseeko(%s, %s) did not fail, set pos to %jd", \
+ __STRING(offset), __STRING(whence), \
+ (intmax_t)ftello(fp)); \
+ ATF_REQUIRE_MSG(errno == (error), \
+ "fseeko(%s, %s) failed with %d rather than %s", \
+ __STRING(offset), __STRING(whence), errno, \
+ __STRING(error)); \
+} while (0)
+
+#define SEEK_OK(offset, whence, result) do { \
+ ATF_REQUIRE_MSG(fseeko(fp, (offset), (whence)) == 0, \
+ "fseeko(%s, %s) failed: %s", \
+ __STRING(offset), __STRING(whence), strerror(errno)); \
+ ATF_REQUIRE_MSG(ftello(fp) == (result), \
+ "fseeko(%s, %s) seeked to %jd rather than %s", \
+ __STRING(offset), __STRING(whence), \
+ (intmax_t)ftello(fp), __STRING(result)); \
+} while (0)
+
+ SEEK_FAIL(-1, SEEK_SET, EINVAL);
+ SEEK_FAIL(-1, SEEK_CUR, EINVAL);
+ SEEK_FAIL(-1, SEEK_END, EINVAL);
+ fwprintf(fp, L"foo");
+ SEEK_OK(-1, SEEK_CUR, 2);
+ SEEK_OK(0, SEEK_SET, 0);
+ SEEK_OK(-1, SEEK_END, 2);
+ SEEK_OK(OFF_MAX - 1, SEEK_SET, OFF_MAX - 1);
+ SEEK_FAIL(2, SEEK_CUR, EOVERFLOW);
+ fclose(fp);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, open_group_test);
+ ATF_TP_ADD_TC(tp, simple_tests);
+ ATF_TP_ADD_TC(tp, seek_tests);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdio/perror_test.c b/lib/libc/tests/stdio/perror_test.c
new file mode 100644
index 000000000000..f3cb646d094e
--- /dev/null
+++ b/lib/libc/tests/stdio/perror_test.c
@@ -0,0 +1,104 @@
+/*-
+ * Copyright (c) 2002 Tim J. Robbins
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test program for perror() as specified by IEEE Std. 1003.1-2001 and
+ * ISO/IEC 9899:1999.
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+static char tmpfil[PATH_MAX];
+
+ATF_TC_WITHOUT_HEAD(perror_test);
+ATF_TC_BODY(perror_test, tc)
+{
+ char lbuf[512];
+ int i;
+ char *s;
+
+ strcpy(tmpfil, "perror.XXXXXXXX");
+ ATF_REQUIRE(mkstemp(tmpfil) >= 0);
+ /* Reopen stderr on a file descriptor other than 2. */
+ fclose(stderr);
+ for (i = 0; i < 3; i++)
+ dup(0);
+ ATF_REQUIRE(freopen(tmpfil, "r+", stderr) != NULL);
+
+ /*
+ * Test that perror() doesn't call strerror() (4.4BSD bug),
+ * the two ways of omitting a program name, and the formatting when
+ * a program name is specified.
+ */
+ s = strerror(ENOENT);
+ ATF_REQUIRE_MSG(strcmp(s, "No such file or directory") == 0,
+ "message obtained was: %s", s);
+ errno = EPERM;
+ perror(NULL);
+ perror("");
+ perror("perror_test");
+ ATF_REQUIRE_MSG(strcmp(s, "No such file or directory") == 0,
+ "message obtained was: %s", s);
+
+ /*
+ * Read it back to check...
+ */
+ rewind(stderr);
+ s = fgets(lbuf, sizeof(lbuf), stderr);
+ ATF_REQUIRE(s != NULL);
+ ATF_REQUIRE_MSG(strcmp(s, "Operation not permitted\n") == 0,
+ "message obtained was: %s", s);
+ s = fgets(lbuf, sizeof(lbuf), stderr);
+ ATF_REQUIRE(s != NULL);
+ ATF_REQUIRE_MSG(strcmp(s, "Operation not permitted\n") == 0,
+ "message obtained was: %s", s);
+ s = fgets(lbuf, sizeof(lbuf), stderr);
+ ATF_REQUIRE(s != NULL);
+ ATF_REQUIRE_MSG(
+ strcmp(s, "perror_test: Operation not permitted\n") == 0,
+ "message obtained was: %s", s);
+ s = fgets(lbuf, sizeof(lbuf), stderr);
+ ATF_REQUIRE(s == NULL);
+ fclose(stderr);
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, perror_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdio/print_positional_test.c b/lib/libc/tests/stdio/print_positional_test.c
new file mode 100644
index 000000000000..f896a30882f4
--- /dev/null
+++ b/lib/libc/tests/stdio/print_positional_test.c
@@ -0,0 +1,154 @@
+/* $OpenBSD: sprintf_test.c,v 1.3 2004/09/16 20:22:26 otto Exp $ */
+
+/*
+ * Copyright (c) 2003 Theo de Raadt
+ * 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 HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <atf-c.h>
+
+const char correct[] =
+ "|xx 01 02 03 04\n"
+ "|xx 05 06 07 08\n"
+ "|xx 09 10 11 12\n"
+ "|xx 13 14 15 16\n"
+ "|xx 17 18 19 20\n"
+ "|xx 21 22 23 24\n"
+ "|xx 25 26 27 28\n"
+ "|xx 29 30 31 32\n"
+ "|xx 33 34 35 36\n"
+ "|xx 37 38 39 40\n"
+ "|xx 41 42 43 44\n"
+ "|xx 45 -1 1 -1 1\n";
+
+const char correct2[] =
+ "b bs BSD";
+static char buf[1024];
+static wchar_t wbuf1[1024], wbuf2[1024];
+static const char *temp;
+
+ATF_TC_WITHOUT_HEAD(positional_normal);
+ATF_TC_BODY(positional_normal, tc)
+{
+
+ /* Test positional arguments */
+ snprintf(buf, sizeof buf,
+ "|xx %1$s %2$s %3$s %4$s\n"
+ "|xx %5$s %6$s %7$s %8$s\n"
+ "|xx %9$s %10$s %11$s %12$s\n"
+ "|xx %13$s %14$s %15$s %16$s\n"
+ "|xx %17$s %18$s %19$s %20$s\n"
+ "|xx %21$s %22$s %23$s %24$s\n"
+ "|xx %25$s %26$s %27$s %28$s\n"
+ "|xx %29$s %30$s %31$s %32$s\n"
+ "|xx %33$s %34$s %35$s %36$s\n"
+ "|xx %37$s %38$s %39$s %40$s\n"
+ "|xx %41$s %42$s %43$s %44$s\n"
+ "|xx %45$d %46$ld %47$lld %48$d %49$lld\n",
+ "01", "02", "03", "04", "05", "06",
+ "07", "08", "09", "10", "11", "12",
+ "13", "14", "15", "16", "17", "18",
+ "19", "20", "21", "22", "23", "24",
+ "25", "26", "27", "28", "29", "30",
+ "31", "32", "33", "34", "35", "36",
+ "37", "38", "39", "40", "41", "42",
+ "43", "44", 45, -1L, 1LL, -1, 1LL
+ );
+ ATF_REQUIRE_MSG(wcscmp(wbuf1, wbuf2) == 0,
+ "buffers didn't match");
+}
+
+ATF_TC_WITHOUT_HEAD(positional_wide);
+ATF_TC_BODY(positional_wide, tc)
+{
+
+ swprintf(wbuf1, nitems(wbuf1),
+ L"|xx %1$s %2$s %3$s %4$s\n"
+ "|xx %5$s %6$s %7$s %8$s\n"
+ "|xx %9$s %10$s %11$s %12$s\n"
+ "|xx %13$s %14$s %15$s %16$s\n"
+ "|xx %17$s %18$s %19$s %20$s\n"
+ "|xx %21$s %22$s %23$s %24$s\n"
+ "|xx %25$s %26$s %27$s %28$s\n"
+ "|xx %29$s %30$s %31$s %32$s\n"
+ "|xx %33$s %34$s %35$s %36$s\n"
+ "|xx %37$s %38$s %39$s %40$s\n"
+ "|xx %41$s %42$s %43$s %44$s\n"
+ "|xx %45$d %46$ld %47$lld %48$d %49$lld\n",
+ "01", "02", "03", "04", "05", "06",
+ "07", "08", "09", "10", "11", "12",
+ "13", "14", "15", "16", "17", "18",
+ "19", "20", "21", "22", "23", "24",
+ "25", "26", "27", "28", "29", "30",
+ "31", "32", "33", "34", "35", "36",
+ "37", "38", "39", "40", "41", "42",
+ "43", "44", 45, -1L, 1LL, -1, 1LL
+ );
+ temp = correct;
+ mbsrtowcs(wbuf2, &temp, nitems(wbuf2), NULL);
+ ATF_REQUIRE_MSG(wcscmp(wbuf1, wbuf2) == 0,
+ "buffers didn't match");
+}
+
+ATF_TC_WITHOUT_HEAD(positional_precision);
+ATF_TC_BODY(positional_precision, tc)
+{
+
+ snprintf(buf, sizeof buf, "%2$.*4$s %2$.*3$s %1$s",
+ "BSD", "bsd", 2, 1);
+ ATF_REQUIRE_MSG(strcmp(buf, correct2) == 0,
+ "buffers didn't match");
+}
+
+ATF_TC_WITHOUT_HEAD(positional_precision_wide);
+ATF_TC_BODY(positional_precision_wide, tc)
+{
+
+ swprintf(wbuf1, sizeof buf, L"%2$.*4$s %2$.*3$s %1$s",
+ "BSD", "bsd", 2, 1);
+ temp = correct2;
+ mbsrtowcs(wbuf2, &temp, nitems(wbuf2), NULL);
+ ATF_REQUIRE_MSG(wcscmp(wbuf1, wbuf2) == 0,
+ "buffers didn't match");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, positional_normal);
+ ATF_TP_ADD_TC(tp, positional_wide);
+ ATF_TP_ADD_TC(tp, positional_precision);
+ ATF_TP_ADD_TC(tp, positional_precision_wide);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdio/printbasic_test.c b/lib/libc/tests/stdio/printbasic_test.c
new file mode 100644
index 000000000000..411dff518795
--- /dev/null
+++ b/lib/libc/tests/stdio/printbasic_test.c
@@ -0,0 +1,159 @@
+/*-
+ * Copyright (c) 2009 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Tests for basic and miscellaneous printf() formats.
+ */
+
+#include <err.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <atf-c.h>
+
+#define S_UINT64MAX "18446744073709551615"
+#define S_UINT32MAX "4294967295"
+#define S_INT64MIN "-9223372036854775808"
+#define S_INT32MIN "-2147483648"
+
+#define S_SIZEMAX (SIZE_MAX == UINT64_MAX ? S_UINT64MAX : S_UINT32MAX)
+#define S_ULONGMAX (ULONG_MAX == UINT64_MAX ? S_UINT64MAX : S_UINT32MAX)
+#define S_ULLONGMAX (ULLONG_MAX == UINT64_MAX ? S_UINT64MAX : S_UINT32MAX)
+
+static void
+smash_stack(void)
+{
+ static uint32_t junk = 0xdeadbeef;
+ uint32_t buf[512];
+ int i;
+
+ for (i = 0; i < sizeof(buf) / sizeof(buf[0]); i++)
+ buf[i] = junk;
+}
+
+#define testfmt(result, fmt, ...) \
+ _testfmt((result), #__VA_ARGS__, fmt, __VA_ARGS__)
+static void
+_testfmt(const char *result, const char *argstr, const char *fmt,...)
+{
+#define BUF 100
+ wchar_t ws[BUF], wfmt[BUF], wresult[BUF];
+ char s[BUF];
+ va_list ap, ap2;
+
+ va_start(ap, fmt);
+ va_copy(ap2, ap);
+ smash_stack();
+ vsnprintf(s, sizeof(s), fmt, ap);
+ ATF_CHECK_MSG(strcmp(result, s) == 0,
+ "printf(\"%s\", %s) ==> [%s], expected [%s]",
+ fmt, argstr, s, result);
+
+ smash_stack();
+ mbstowcs(ws, s, BUF - 1);
+ mbstowcs(wfmt, fmt, BUF - 1);
+ mbstowcs(wresult, result, BUF - 1);
+ vswprintf(ws, sizeof(ws) / sizeof(ws[0]), wfmt, ap2);
+ ATF_CHECK_MSG(wcscmp(wresult, ws) == 0,
+ "wprintf(\"%ls\", %s) ==> [%ls], expected [%ls]",
+ wfmt, argstr, ws, wresult);
+
+ va_end(ap);
+ va_end(ap2);
+}
+
+ATF_TC_WITHOUT_HEAD(int_within_limits);
+ATF_TC_BODY(int_within_limits, tc)
+{
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "C"));
+
+ /* The test requires these to be true. */
+ ATF_REQUIRE(UINTMAX_MAX == UINT64_MAX);
+ ATF_REQUIRE(UINT_MAX == UINT32_MAX);
+ ATF_REQUIRE(USHRT_MAX == 0xffff);
+ ATF_REQUIRE(UCHAR_MAX == 0xff);
+
+ /* Make sure we handle signed vs. unsigned args correctly. */
+ testfmt("-1", "%jd", (intmax_t)-1);
+ testfmt(S_UINT64MAX, "%ju", UINT64_MAX);
+
+ if (sizeof(ptrdiff_t) != sizeof(uintmax_t))
+ atf_tc_expect_fail("the %%t qualifier is broken on 32-bit "
+ "platforms where there's a mismatch between ptrdiff_t and "
+ "uintmax_t's type width; bug # 191674");
+
+ testfmt("-1", "%td", (ptrdiff_t)-1);
+ testfmt(S_SIZEMAX, "%tu", (size_t)-1);
+
+ testfmt("-1", "%zd", (ssize_t)-1);
+ testfmt(S_SIZEMAX, "%zu", (ssize_t)-1);
+
+ testfmt("-1", "%ld", (long)-1);
+ testfmt(S_ULONGMAX, "%lu", ULONG_MAX);
+
+ testfmt("-1", "%lld", (long long)-1);
+ testfmt(S_ULLONGMAX, "%llu", ULLONG_MAX);
+
+ testfmt("-1", "%d", -1);
+ testfmt(S_UINT32MAX, "%u", UINT32_MAX);
+
+ testfmt("-1", "%hd", -1);
+ testfmt("65535", "%hu", USHRT_MAX);
+
+ testfmt("-1", "%hhd", -1);
+ testfmt("255", "%hhu", UCHAR_MAX);
+}
+
+ATF_TC_WITHOUT_HEAD(int_limits);
+ATF_TC_BODY(int_limits, tc)
+{
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "C"));
+
+ /*
+ * Check that printing the largest negative number does not cause
+ * overflow when it is negated.
+ */
+ testfmt(S_INT32MIN, "%d", INT_MIN);
+ testfmt(S_INT64MIN, "%jd", INTMAX_MIN);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, int_within_limits);
+ ATF_TP_ADD_TC(tp, int_limits);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdio/printfloat_test.c b/lib/libc/tests/stdio/printfloat_test.c
new file mode 100644
index 000000000000..4493fe1c15d3
--- /dev/null
+++ b/lib/libc/tests/stdio/printfloat_test.c
@@ -0,0 +1,432 @@
+/*-
+ * Copyright (c) 2002-2009 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test for printf() floating point formats.
+ */
+
+#include <err.h>
+#include <fenv.h>
+#include <float.h>
+#include <locale.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <atf-c.h>
+
+static void
+smash_stack(void)
+{
+ static uint32_t junk = 0xdeadbeef;
+ uint32_t buf[512];
+ size_t i;
+
+ for (i = 0; i < sizeof(buf) / sizeof(buf[0]); i++)
+ buf[i] = junk;
+}
+
+#define testfmt(result, fmt, ...) \
+ _testfmt((result), #__VA_ARGS__, fmt, __VA_ARGS__)
+static void
+_testfmt(const char *result, const char *argstr, const char *fmt,...)
+{
+#define BUF 100
+ wchar_t ws[BUF], wfmt[BUF], wresult[BUF];
+ char s[BUF];
+ va_list ap, ap2;
+
+ va_start(ap, fmt);
+ va_copy(ap2, ap);
+ smash_stack();
+ vsnprintf(s, sizeof(s), fmt, ap);
+ ATF_CHECK_MSG(strcmp(result, s) == 0,
+ "printf(\"%s\", %s) ==> [%s], expected [%s]",
+ fmt, argstr, s, result);
+
+ smash_stack();
+ mbstowcs(ws, s, BUF - 1);
+ mbstowcs(wfmt, fmt, BUF - 1);
+ mbstowcs(wresult, result, BUF - 1);
+ vswprintf(ws, sizeof(ws) / sizeof(ws[0]), wfmt, ap2);
+ ATF_CHECK_MSG(wcscmp(wresult, ws) == 0,
+ "wprintf(\"%ls\", %s) ==> [%ls], expected [%ls]",
+ wfmt, argstr, ws, wresult);
+
+ va_end(ap);
+ va_end(ap2);
+}
+
+ATF_TC_WITHOUT_HEAD(float_within_limits);
+ATF_TC_BODY(float_within_limits, tc)
+{
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "C"));
+
+ /* Basic tests of decimal output functionality. */
+ testfmt(" 1.000000E+00", "%13E", 1.0);
+ testfmt(" 1.000000", "%13f", 1.0);
+ testfmt(" 1", "%13G", 1.0);
+ testfmt(" 1.000000E+00", "%13LE", 1.0L);
+ testfmt(" 1.000000", "%13Lf", 1.0L);
+ testfmt(" 1", "%13LG", 1.0L);
+
+ testfmt("2.718282", "%.*f", -2, 2.7182818);
+
+ testfmt("1.234568e+06", "%e", 1234567.8);
+ testfmt("1234567.800000", "%f", 1234567.8);
+ testfmt("1.23457E+06", "%G", 1234567.8);
+ testfmt("1.234568e+06", "%Le", 1234567.8L);
+ testfmt("1234567.800000", "%Lf", 1234567.8L);
+ testfmt("1.23457E+06", "%LG", 1234567.8L);
+
+#if (LDBL_MANT_DIG > DBL_MANT_DIG) && !defined(__i386__)
+ testfmt("123456789.864210", "%Lf", 123456789.8642097531L);
+ testfmt("-1.23457E+08", "%LG", -123456789.8642097531L);
+ testfmt("123456789.8642097531", "%.10Lf", 123456789.8642097531L);
+ testfmt(" 3.141592653589793238e-4000", "%L27.18Le",
+ 3.14159265358979323846e-4000L);
+#endif
+}
+
+ATF_TC_WITHOUT_HEAD(infinities_and_nans);
+ATF_TC_BODY(infinities_and_nans, tc)
+{
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "C"));
+
+ testfmt("nan", "%e", NAN);
+ testfmt("NAN", "%F", NAN);
+ testfmt("nan", "%g", NAN);
+ testfmt("NAN", "%LE", (long double)NAN);
+ testfmt(" nan", "%05e", NAN);
+
+ testfmt("INF", "%E", HUGE_VAL);
+ testfmt("-inf", "%f", -HUGE_VAL);
+ testfmt("+inf", "%+g", HUGE_VAL);
+ testfmt(" inf", "%4.2Le", HUGE_VALL);
+ testfmt("-inf", "%Lf", -HUGE_VALL);
+ testfmt(" inf", "%05e", HUGE_VAL);
+ testfmt(" -inf", "%05e", -HUGE_VAL);
+}
+
+ATF_TC_WITHOUT_HEAD(padding);
+ATF_TC_BODY(padding, tc)
+{
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "C"));
+
+ testfmt("0.000000e+00", "%e", 0.0);
+ testfmt("0.000000", "%F", (double)0.0);
+ testfmt("0", "%G", 0.0);
+ testfmt(" 0", "%3.0Lg", 0.0L);
+ testfmt(" 0", "%5.0f", 0.001);
+}
+
+ATF_TC_WITHOUT_HEAD(precision_specifiers);
+ATF_TC_BODY(precision_specifiers, tc)
+{
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "C"));
+
+ testfmt("1.0123e+00", "%.4e", 1.0123456789);
+ testfmt("1.0123", "%.4f", 1.0123456789);
+ testfmt("1.012", "%.4g", 1.0123456789);
+ testfmt("1.2346e-02", "%.4e", 0.0123456789);
+ testfmt("0.0123", "%.4f", 0.0123456789);
+ testfmt("0.01235", "%.4g", 0.0123456789);
+}
+
+ATF_TC_WITHOUT_HEAD(thousands_separator_and_other_locale_tests);
+ATF_TC_BODY(thousands_separator_and_other_locale_tests, tc)
+{
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "C"));
+
+ testfmt("12345678.0625", "%'.04f", 12345678.0625);
+ testfmt("0012345678.0625", "%'015.4F", 12345678.0625);
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "hi_IN.ISCII-DEV")); /* grouping == 2;3 */
+ testfmt("1,23,45,678.0625", "%'.4f", 12345678.0625);
+ testfmt("01,23,45,678.0625", "%'017.4F", 12345678.0625);
+ testfmt(" 9,000", "%'6.0f", 9000.0);
+ testfmt("9,000.0", "%'.1f", 9000.0);
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "ru_RU.ISO8859-5")); /* decimalpoint==, */
+ testfmt("3,1415", "%g", 3.1415);
+
+ /* thousands=. decimalpoint=, grouping=3;3 */
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "el_GR.ISO8859-7")); /* decimalpoint==, */
+ testfmt("1.234,00", "%'.2f", 1234.00);
+ testfmt("123.456,789", "%'.3f", 123456.789);
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "C"));
+ testfmt("12345678.062500", "%'f", 12345678.0625);
+ testfmt("9000.000000", "%'f", 9000.0);
+}
+
+ATF_TC_WITHOUT_HEAD(signed_conversions);
+ATF_TC_BODY(signed_conversions, tc)
+{
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "C"));
+
+ testfmt("+2.500000e-01", "%+e", 0.25);
+ testfmt("+0.000000", "%+F", 0.0);
+ testfmt("-1", "%+g", -1.0);
+
+ testfmt("-1.000000e+00", "% e", -1.0);
+ testfmt("+1.000000", "% +f", 1.0);
+ testfmt(" 1", "% g", 1.0);
+ testfmt(" 0", "% g", 0.0);
+}
+
+ATF_TC_WITHOUT_HEAD(alternate_form);
+ATF_TC_BODY(alternate_form, tc)
+{
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "C"));
+
+ testfmt("1.250e+00", "%#.3e", 1.25);
+ testfmt("123.000000", "%#f", 123.0);
+ testfmt(" 12345.", "%#7.5g", 12345.0);
+ testfmt(" 1.00000", "%#8g", 1.0);
+ testfmt("0.0", "%#.2g", 0.0);
+}
+
+ATF_TC_WITHOUT_HEAD(padding_and_decimal_point_placement);
+ATF_TC_BODY(padding_and_decimal_point_placement, tc)
+{
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "C"));
+
+ testfmt("03.2E+00", "%08.1E", 3.25);
+ testfmt("003.25", "%06.2F", 3.25);
+ testfmt("0003.25", "%07.4G", 3.25);
+
+ testfmt("3.14159e-05", "%g", 3.14159e-5);
+ testfmt("0.000314159", "%g", 3.14159e-4);
+ testfmt("3.14159e+06", "%g", 3.14159e6);
+ testfmt("314159", "%g", 3.14159e5);
+ testfmt("314159.", "%#g", 3.14159e5);
+
+ testfmt(" 9.000000e+03", "%13e", 9000.0);
+ testfmt(" 9000.000000", "%12f", 9000.0);
+ testfmt(" 9000", "%5g", 9000.0);
+ testfmt(" 900000.", "%#8g", 900000.0);
+ testfmt(" 9e+06", "%6g", 9000000.0);
+ testfmt(" 9.000000e-04", "%13e", 0.0009);
+ testfmt(" 0.000900", "%9f", 0.0009);
+ testfmt(" 0.0009", "%7g", 0.0009);
+ testfmt(" 9e-05", "%6g", 0.00009);
+ testfmt(" 9.00000e-05", "%#12g", 0.00009);
+ testfmt(" 9.e-05", "%#7.1g", 0.00009);
+
+ testfmt(" 0.0", "%4.1f", 0.0);
+ testfmt("90.0", "%4.1f", 90.0);
+ testfmt(" 100", "%4.0f", 100.0);
+ testfmt("9.0e+01", "%4.1e", 90.0);
+ testfmt("1e+02", "%4.0e", 100.0);
+}
+
+ATF_TC_WITHOUT_HEAD(decimal_rounding);
+ATF_TC_BODY(decimal_rounding, tc)
+{
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "C"));
+
+ fesetround(FE_DOWNWARD);
+ testfmt("4.437", "%.3f", 4.4375);
+ testfmt("-4.438", "%.3f", -4.4375);
+ testfmt("4.437", "%.3Lf", 4.4375L);
+ testfmt("-4.438", "%.3Lf", -4.4375L);
+
+ fesetround(FE_UPWARD);
+ testfmt("4.438", "%.3f", 4.4375);
+ testfmt("-4.437", "%.3f", -4.4375);
+ testfmt("4.438", "%.3Lf", 4.4375L);
+ testfmt("-4.437", "%.3Lf", -4.4375L);
+
+ fesetround(FE_TOWARDZERO);
+ testfmt("4.437", "%.3f", 4.4375);
+ testfmt("-4.437", "%.3f", -4.4375);
+ testfmt("4.437", "%.3Lf", 4.4375L);
+ testfmt("-4.437", "%.3Lf", -4.4375L);
+
+ fesetround(FE_TONEAREST);
+ testfmt("4.438", "%.3f", 4.4375);
+ testfmt("-4.438", "%.3f", -4.4375);
+ testfmt("4.438", "%.3Lf", 4.4375L);
+ testfmt("-4.438", "%.3Lf", -4.4375L);
+}
+
+ATF_TC_WITHOUT_HEAD(hexadecimal_floating_point);
+ATF_TC_BODY(hexadecimal_floating_point, tc)
+{
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "C"));
+
+ /*
+ * Hexadecimal floating point (%a, %A) tests. Some of these
+ * are only valid if the implementation converts to hex digits
+ * on nibble boundaries.
+ */
+ testfmt("0x0p+0", "%a", 0x0.0p0);
+ testfmt("0X0.P+0", "%#LA", 0x0.0p0L);
+ testfmt("inf", "%La", (long double)INFINITY);
+ testfmt("+INF", "%+A", INFINITY);
+ testfmt("nan", "%La", (long double)NAN);
+ testfmt("NAN", "%A", NAN);
+
+ testfmt(" 0x1.23p+0", "%10a", 0x1.23p0);
+ testfmt(" 0x1.23p-500", "%12a", 0x1.23p-500);
+ testfmt(" 0x1.2p+40", "%10.1a", 0x1.23p40);
+ testfmt(" 0X1.230000000000000000000000P-4", "%32.24A", 0x1.23p-4);
+ testfmt("0x1p-1074", "%a", 0x1p-1074);
+ testfmt("0x1.2345p-1024", "%a", 0x1.2345p-1024);
+
+#if (LDBL_MANT_DIG == 64)
+ testfmt("0x1.921fb54442d18468p+1", "%La", 0x3.243f6a8885a308dp0L);
+ testfmt("0x1p-16445", "%La", 0x1p-16445L);
+ testfmt("0x1.30ecap-16381", "%La", 0x9.8765p-16384L);
+#elif (LDBL_MANT_DIG == 113)
+ testfmt("0x1.921fb54442d18469898cc51701b8p+1", "%La",
+ 0x3.243f6a8885a308d313198a2e037p0L);
+ testfmt("0x1p-16494", "%La", 0x1p-16494L);
+ testfmt("0x1.2345p-16384", "%La", 0x1.2345p-16384L);
+#else
+ testfmt("0x1.921fb54442d18p+1", "%La", 0x3.243f6a8885a31p0L);
+ testfmt("0x1p-1074", "%La", 0x1p-1074L);
+ testfmt("0x1.30ecap-1021", "%La", 0x9.8765p-1024L);
+#endif
+}
+
+ATF_TC_WITHOUT_HEAD(hexadecimal_rounding);
+ATF_TC_BODY(hexadecimal_rounding, tc)
+{
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "C"));
+
+ fesetround(FE_TOWARDZERO);
+ testfmt("0X1.23456789ABCP+0", "%.11A", 0x1.23456789abcdep0);
+ testfmt("-0x1.23456p+0", "%.5a", -0x1.23456789abcdep0);
+ testfmt("0x1.23456p+0", "%.5a", 0x1.23456789abcdep0);
+ testfmt("0x1.234567p+0", "%.6a", 0x1.23456789abcdep0);
+ testfmt("-0x1.234566p+0", "%.6a", -0x1.23456689abcdep0);
+
+ fesetround(FE_DOWNWARD);
+ testfmt("0X1.23456789ABCP+0", "%.11A", 0x1.23456789abcdep0);
+ testfmt("-0x1.23457p+0", "%.5a", -0x1.23456789abcdep0);
+ testfmt("0x1.23456p+0", "%.5a", 0x1.23456789abcdep0);
+ testfmt("0x1.234567p+0", "%.6a", 0x1.23456789abcdep0);
+ testfmt("-0x1.234567p+0", "%.6a", -0x1.23456689abcdep0);
+
+ fesetround(FE_UPWARD);
+ testfmt("0X1.23456789ABDP+0", "%.11A", 0x1.23456789abcdep0);
+ testfmt("-0x1.23456p+0", "%.5a", -0x1.23456789abcdep0);
+ testfmt("0x1.23457p+0", "%.5a", 0x1.23456789abcdep0);
+ testfmt("0x1.234568p+0", "%.6a", 0x1.23456789abcdep0);
+ testfmt("-0x1.234566p+0", "%.6a", -0x1.23456689abcdep0);
+
+ fesetround(FE_TONEAREST);
+ testfmt("0x1.23456789abcdep+4", "%a", 0x1.23456789abcdep4);
+ testfmt("0X1.23456789ABDP+0", "%.11A", 0x1.23456789abcdep0);
+ testfmt("-0x1.23456p+0", "%.5a", -0x1.23456789abcdep0);
+ testfmt("0x1.23456p+0", "%.5a", 0x1.23456789abcdep0);
+ testfmt("0x1.234568p+0", "%.6a", 0x1.23456789abcdep0);
+ testfmt("-0x1.234567p+0", "%.6a", -0x1.23456689abcdep0);
+ testfmt("0x1.00p-1029", "%.2a", 0x1.fffp-1030);
+ testfmt("0x1.00p-1026", "%.2a", 0xf.fffp-1030);
+ testfmt("0x1.83p+0", "%.2a", 1.51);
+}
+
+ATF_TC_WITHOUT_HEAD(subnormal_double);
+ATF_TC_BODY(subnormal_double, tc)
+{
+ /* Regression test for https://bugs.freebsd.org/253847 */
+ double positive = __DBL_DENORM_MIN__;
+ testfmt("4.9406564584124654418e-324", "%20.20g", positive);
+ testfmt("4.9406564584124654418E-324", "%20.20G", positive);
+ testfmt("0x1p-1074", "%a", positive);
+ testfmt("0X1P-1074", "%A", positive);
+ double negative = -__DBL_DENORM_MIN__;
+ testfmt("-4.9406564584124654418e-324", "%20.20g", negative);
+ testfmt("-4.9406564584124654418E-324", "%20.20G", negative);
+ testfmt("-0x1p-1074", "%a", negative);
+ testfmt("-0X1P-1074", "%A", negative);
+}
+
+ATF_TC_WITHOUT_HEAD(subnormal_float);
+ATF_TC_BODY(subnormal_float, tc)
+{
+ float positive = __FLT_DENORM_MIN__;
+ testfmt("1.4012984643248170709e-45", "%20.20g", positive);
+ testfmt("1.4012984643248170709E-45", "%20.20G", positive);
+ testfmt("0x1p-149", "%a", positive);
+ testfmt("0X1P-149", "%A", positive);
+ float negative = -__FLT_DENORM_MIN__;
+ testfmt("-1.4012984643248170709e-45", "%20.20g", negative);
+ testfmt("-1.4012984643248170709E-45", "%20.20G", negative);
+ testfmt("-0x1p-149", "%a", negative);
+ testfmt("-0X1P-149", "%A", negative);
+}
+
+ATF_TC_WITHOUT_HEAD(hexadecimal_rounding_fullprec);
+ATF_TC_BODY(hexadecimal_rounding_fullprec, tc)
+{
+ /* Double: %.13a with binary64 mantissa=53 */
+ testfmt("0x1.1234567890bbbp+0", "%.13a", 0x1.1234567890bbbp+0);
+
+#if defined(__aarch64__)
+ /* On arm64, long double is IEEE binary128 (mantissa=113) */
+ testfmt("0x1.3c0ca428c59fbbbbbbbbbbbbbbbbp+0", "%.28La", 0x1.3c0ca428c59fbbbbbbbbbbbbbbbbp+0L);
+#endif
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, float_within_limits);
+ ATF_TP_ADD_TC(tp, infinities_and_nans);
+ ATF_TP_ADD_TC(tp, padding);
+ ATF_TP_ADD_TC(tp, precision_specifiers);
+ ATF_TP_ADD_TC(tp, thousands_separator_and_other_locale_tests);
+ ATF_TP_ADD_TC(tp, signed_conversions);
+ ATF_TP_ADD_TC(tp, alternate_form);
+ ATF_TP_ADD_TC(tp, padding_and_decimal_point_placement);
+ ATF_TP_ADD_TC(tp, decimal_rounding);
+ ATF_TP_ADD_TC(tp, hexadecimal_floating_point);
+ ATF_TP_ADD_TC(tp, hexadecimal_rounding);
+ ATF_TP_ADD_TC(tp, subnormal_double);
+ ATF_TP_ADD_TC(tp, subnormal_float);
+ ATF_TP_ADD_TC(tp, hexadecimal_rounding_fullprec);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdio/scanfloat_test.c b/lib/libc/tests/stdio/scanfloat_test.c
new file mode 100644
index 000000000000..635d93d560b3
--- /dev/null
+++ b/lib/libc/tests/stdio/scanfloat_test.c
@@ -0,0 +1,313 @@
+/*-
+ * Copyright (C) 2003, 2005 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test for scanf() floating point formats.
+ */
+
+#include <fenv.h>
+#include <float.h>
+#include <locale.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#define eq(type, a, b) _eq(type##_EPSILON, (a), (b))
+static int
+_eq(long double epsilon, long double a, long double b)
+{
+ long double delta;
+
+ delta = fabsl(a - b);
+ return (delta <= epsilon);
+}
+
+ATF_TC_WITHOUT_HEAD(normalized_numbers);
+ATF_TC_BODY(normalized_numbers, tc)
+{
+ char buf[128];
+ long double ld = 0.0;
+ double d = 0.0;
+ float f = 0.0;
+
+ buf[0] = '\0';
+ ATF_REQUIRE(setlocale(LC_NUMERIC, ""));
+
+ ATF_REQUIRE_EQ(1, sscanf("3.141592", "%e", &f));
+ ATF_REQUIRE(eq(FLT, f, 3.141592));
+
+ ATF_REQUIRE_EQ(1, sscanf("3.141592653589793", "%lf", &d));
+ ATF_REQUIRE(eq(DBL, d, 3.141592653589793));
+
+ ATF_REQUIRE_EQ(1, sscanf("1.234568e+06", "%E", &f));
+ ATF_REQUIRE(eq(FLT, f, 1.234568e+06));
+
+ ATF_REQUIRE_EQ(1, sscanf("-1.234568e6", "%lF", &d));
+ ATF_REQUIRE(eq(DBL, d, -1.234568e6));
+
+ ATF_REQUIRE_EQ(1, sscanf("+1.234568e-52", "%LG", &ld));
+ ATF_REQUIRE(eq(LDBL, ld, 1.234568e-52L));
+
+ ATF_REQUIRE_EQ(1, sscanf("0.1", "%la", &d));
+ ATF_REQUIRE(eq(DBL, d, 0.1));
+
+ ATF_REQUIRE_EQ(1, sscanf("00.2", "%lA", &d));
+ ATF_REQUIRE(eq(DBL, d, 0.2));
+
+ ATF_REQUIRE_EQ(2, sscanf("123456", "%5le%s", &d, buf));
+ ATF_REQUIRE(eq(DBL, d, 12345.));
+ ATF_REQUIRE(strcmp(buf, "6") == 0);
+
+ ATF_REQUIRE_EQ(1, sscanf("1.0Q", "%*5le%s", buf));
+ ATF_REQUIRE(strcmp(buf, "Q") == 0);
+
+ ATF_REQUIRE_EQ(2, sscanf("-1.23e", "%e%s", &f, buf));
+ ATF_REQUIRE(eq(FLT, f, -1.23));
+ ATF_REQUIRE(strcmp(buf, "e") == 0);
+
+ ATF_REQUIRE_EQ(2, sscanf("1.25e+", "%le%s", &d, buf));
+ ATF_REQUIRE(eq(DBL, d, 1.25));
+ ATF_REQUIRE(strcmp(buf, "e+") == 0);
+
+ ATF_REQUIRE_EQ(2, sscanf("1.23E4E5", "%le%s", &d, buf));
+ ATF_REQUIRE(eq(DBL, d, 1.23e4));
+ ATF_REQUIRE(strcmp(buf, "E5") == 0);
+
+ ATF_REQUIRE_EQ(1, sscanf("12e6", "%le", &d));
+ ATF_REQUIRE(eq(DBL, d, 12e6));
+
+ ATF_REQUIRE_EQ(2, sscanf("1.a", "%le%s", &d, buf));
+ ATF_REQUIRE(eq(DBL, d, 1.0));
+ ATF_REQUIRE(strcmp(buf, "a") == 0);
+
+ ATF_REQUIRE_EQ(2, sscanf(".0p4", "%le%s", &d, buf));
+ ATF_REQUIRE(eq(DBL, d, 0.0));
+ ATF_REQUIRE(strcmp(buf, "p4") == 0);
+
+ d = 0.25;
+ ATF_REQUIRE_EQ(0, sscanf(".", "%le", &d));
+ ATF_REQUIRE(d == 0.25);
+
+ ATF_REQUIRE_EQ(1, sscanf("0x08", "%le", &d));
+ ATF_REQUIRE(d == 0x8p0);
+
+ ATF_REQUIRE_EQ(2, sscanf("0x90a.bcdefP+09a", "%le%s", &d, buf));
+ ATF_REQUIRE(d == 0x90a.bcdefp+09);
+ ATF_REQUIRE(strcmp(buf, "a") == 0);
+
+#if (LDBL_MANT_DIG > DBL_MANT_DIG) && !defined(__i386__)
+ ATF_REQUIRE_EQ(1, sscanf("3.14159265358979323846", "%Lg", &ld));
+ ATF_REQUIRE(eq(LDBL, ld, 3.14159265358979323846L));
+
+ ATF_REQUIRE_EQ(2, sscanf(" 0X.0123456789abcdefffp-3g", "%Le%s", &ld, buf));
+ ATF_REQUIRE(ld == 0x0.0123456789abcdefffp-3L);
+ ATF_REQUIRE(strcmp(buf, "g") == 0);
+#endif
+
+ ATF_REQUIRE_EQ(2, sscanf("0xg", "%le%s", &d, buf));
+ ATF_REQUIRE(d == 0.0);
+ ATF_REQUIRE(strcmp(buf, "xg") == 0);
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "ru_RU.ISO8859-5")); /* decimalpoint==, */
+
+ ATF_REQUIRE_EQ(2, sscanf("1.23", "%le%s", &d, buf));
+ ATF_REQUIRE(d == 1.0);
+ ATF_REQUIRE(strcmp(buf, ".23") == 0);
+
+ ATF_REQUIRE_EQ(1, sscanf("1,23", "%le", &d));
+ ATF_REQUIRE(d == 1.23);
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, ""));
+}
+
+ATF_TC_WITHOUT_HEAD(infinities_and_nans);
+ATF_TC_BODY(infinities_and_nans, tc)
+{
+ char buf[128];
+ long double ld = 0.0;
+ double d = 0.0;
+ float f = 0.0;
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "C"));
+
+ ATF_REQUIRE_EQ(1, sscanf("-Inf", "%le", &d));
+ ATF_REQUIRE(d < 0.0 && isinf(d));
+
+ ATF_REQUIRE_EQ(2, sscanf("iNfInItY and beyond", "%le%s", &d, buf));
+ ATF_REQUIRE(d > 0.0 && isinf(d));
+ ATF_REQUIRE(strcmp(buf, " and beyond"));
+
+ ATF_REQUIRE_EQ(1, sscanf("NaN", "%le", &d));
+ ATF_REQUIRE(isnan(d));
+
+ ATF_REQUIRE_EQ(2, sscanf("NAN(123Y", "%le%s", &d, buf));
+ ATF_REQUIRE(isnan(d));
+ ATF_REQUIRE(strcmp(buf, "(123Y") == 0);
+
+ ATF_REQUIRE_EQ(2, sscanf("nan(f00f)plugh", "%le%s", &d, buf));
+ ATF_REQUIRE(isnan(d));
+ ATF_REQUIRE(strcmp(buf, "plugh") == 0);
+
+ ATF_REQUIRE_EQ(1, sscanf("-nan", "%le", &d));
+ ATF_REQUIRE(isnan(d));
+
+ /* Only quiet NaNs should be returned. */
+ ATF_REQUIRE_EQ(1, sscanf("NaN", "%e", &f));
+ ATF_REQUIRE_EQ(1, sscanf("nan", "%le", &d));
+ ATF_REQUIRE_EQ(1, sscanf("nan", "%Le", &ld));
+ ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
+ ATF_REQUIRE(f != f);
+ ATF_REQUIRE(d != d);
+ ATF_REQUIRE(ld != ld);
+ ATF_REQUIRE(fetestexcept(FE_INVALID) == 0);
+ ATF_REQUIRE_EQ(1, sscanf("nan(1234)", "%e", &f));
+ ATF_REQUIRE_EQ(1, sscanf("nan(1234)", "%le", &d));
+ ATF_REQUIRE_EQ(1, sscanf("nan(1234)", "%Le", &ld));
+ ATF_REQUIRE_EQ(0, feclearexcept(FE_ALL_EXCEPT));
+ ATF_REQUIRE(f != f);
+ ATF_REQUIRE(d != d);
+ ATF_REQUIRE(ld != ld);
+ /* POSIX says we should only generate quiet NaNs. */
+ ATF_REQUIRE(fetestexcept(FE_INVALID) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(rounding_tests);
+ATF_TC_BODY(rounding_tests, tc)
+{
+ long double ld = 0.0;
+ double d = 0.0;
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "C"));
+
+ ATF_REQUIRE_EQ(0, fesetround(FE_DOWNWARD));
+
+ ATF_REQUIRE_EQ(1, sscanf("1.999999999999999999999999999999999", "%le", &d));
+ ATF_REQUIRE(d < 2.0);
+ ATF_REQUIRE_EQ(1, sscanf("0x1.ffffffffffffffp0", "%le", &d));
+ ATF_REQUIRE(d < 2.0);
+ ATF_REQUIRE_EQ(1, sscanf("1.999999999999999999999999999999999", "%Le", &ld));
+ ATF_REQUIRE(ld < 2.0);
+
+ ATF_REQUIRE_EQ(1, sscanf("1.0571892669084007", "%le", &d));
+ ATF_REQUIRE(d == 0x1.0ea3f4af0dc59p0);
+ ATF_REQUIRE_EQ(1, sscanf("-1.0571892669084007", "%le", &d));
+ ATF_REQUIRE(d == -0x1.0ea3f4af0dc5ap0);
+ ATF_REQUIRE_EQ(1, sscanf("1.0571892669084010", "%le", &d));
+ ATF_REQUIRE(d == 0x1.0ea3f4af0dc5ap0);
+
+ ATF_REQUIRE_EQ(1, sscanf("0x1.23p-5000", "%le", &d));
+ ATF_REQUIRE(d == 0.0);
+
+ ATF_REQUIRE_EQ(1, sscanf("0x1.2345678p-1050", "%le", &d));
+ ATF_REQUIRE(d == 0x1.234567p-1050);
+
+ ATF_REQUIRE_EQ(0, fesetround(FE_UPWARD));
+
+ ATF_REQUIRE_EQ(1, sscanf("1.0571892669084007", "%le", &d));
+ ATF_REQUIRE(d == 0x1.0ea3f4af0dc5ap0);
+ ATF_REQUIRE_EQ(1, sscanf("-1.0571892669084007", "%le", &d));
+ ATF_REQUIRE(d == -0x1.0ea3f4af0dc59p0);
+ ATF_REQUIRE_EQ(1, sscanf("1.0571892669084010", "%le", &d));
+ ATF_REQUIRE(d == 0x1.0ea3f4af0dc5bp0);
+
+ ATF_REQUIRE_EQ(1, sscanf("0x1.23p-5000", "%le", &d));
+ ATF_REQUIRE(d == 0x1p-1074);
+
+ ATF_REQUIRE_EQ(1, sscanf("0x1.2345678p-1050", "%le", &d));
+ ATF_REQUIRE(d == 0x1.234568p-1050);
+
+ ATF_REQUIRE_EQ(0, fesetround(FE_TOWARDZERO));
+
+ ATF_REQUIRE_EQ(1, sscanf("1.0571892669084007", "%le", &d));
+ ATF_REQUIRE(d == 0x1.0ea3f4af0dc59p0);
+ ATF_REQUIRE_EQ(1, sscanf("-1.0571892669084007", "%le", &d));
+ ATF_REQUIRE(d == -0x1.0ea3f4af0dc59p0);
+ ATF_REQUIRE_EQ(1, sscanf("1.0571892669084010", "%le", &d));
+ ATF_REQUIRE(d == 0x1.0ea3f4af0dc5ap0);
+
+ ATF_REQUIRE_EQ(1, sscanf("0x1.23p-5000", "%le", &d));
+ ATF_REQUIRE(d == 0.0);
+
+ ATF_REQUIRE_EQ(1, sscanf("0x1.2345678p-1050", "%le", &d));
+ ATF_REQUIRE(d == 0x1.234567p-1050);
+
+ ATF_REQUIRE_EQ(0, fesetround(FE_TONEAREST));
+
+ /* 1.0571892669084007 is slightly closer to 0x1.0ea3f4af0dc59p0 */
+ ATF_REQUIRE_EQ(1, sscanf("1.0571892669084007", "%le", &d));
+ ATF_REQUIRE(d == 0x1.0ea3f4af0dc59p0);
+ ATF_REQUIRE_EQ(1, sscanf("-1.0571892669084007", "%le", &d));
+ ATF_REQUIRE(d == -0x1.0ea3f4af0dc59p0);
+ ATF_REQUIRE_EQ(1, sscanf("1.0571892669084010", "%le", &d));
+ ATF_REQUIRE(d == 0x1.0ea3f4af0dc5bp0);
+
+ /* strtod() should round small numbers to 0. */
+ ATF_REQUIRE_EQ(1, sscanf("0x1.23p-5000", "%le", &d));
+ ATF_REQUIRE(d == 0.0);
+
+ /* Extra digits in a denormal shouldn't break anything. */
+ ATF_REQUIRE_EQ(1, sscanf("0x1.2345678p-1050", "%le", &d));
+ ATF_REQUIRE(d == 0x1.234568p-1050);
+}
+
+ATF_TC_WITHOUT_HEAD(strtod);
+ATF_TC_BODY(strtod, tc)
+{
+ char *endp;
+
+ ATF_REQUIRE(setlocale(LC_NUMERIC, "C"));
+
+ ATF_REQUIRE(strtod("0xy", &endp) == 0);
+ ATF_REQUIRE(strcmp("xy", endp) == 0);
+
+ /* This used to cause an infinite loop and round the wrong way. */
+ ATF_REQUIRE_EQ(0, fesetround(FE_DOWNWARD));
+ ATF_REQUIRE(strtof("3.5e38", &endp) == FLT_MAX);
+ ATF_REQUIRE(strtod("2e308", &endp) == DBL_MAX);
+ ATF_REQUIRE_EQ(0, fesetround(FE_UPWARD));
+ ATF_REQUIRE(strtof("3.5e38", &endp) == INFINITY);
+ ATF_REQUIRE(strtod("2e308", &endp) == INFINITY);
+ ATF_REQUIRE_EQ(0, fesetround(FE_TOWARDZERO));
+ ATF_REQUIRE(strtof("3.5e38", &endp) == FLT_MAX);
+ ATF_REQUIRE(strtod("2e308", &endp) == DBL_MAX);
+ ATF_REQUIRE_EQ(0, fesetround(FE_TONEAREST));
+ ATF_REQUIRE(strtof("3.5e38", &endp) == INFINITY);
+ ATF_REQUIRE(strtod("2e308", &endp) == INFINITY);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, normalized_numbers);
+ ATF_TP_ADD_TC(tp, infinities_and_nans);
+ ATF_TP_ADD_TC(tp, rounding_tests);
+ ATF_TP_ADD_TC(tp, strtod);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdio/snprintf_test.c b/lib/libc/tests/stdio/snprintf_test.c
new file mode 100644
index 000000000000..29b908b65120
--- /dev/null
+++ b/lib/libc/tests/stdio/snprintf_test.c
@@ -0,0 +1,205 @@
+/*-
+ * Copyright (c) 2023 Dag-Erling Smørgrav
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <assert.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <atf-c.h>
+
+#ifndef nitems
+#define nitems(a) (sizeof(a) / sizeof(a[0]))
+#endif
+
+#define SNPRINTF_TEST(output, format, ...) \
+ do { \
+ char buf[256]; \
+ assert(strlen(output) < nitems(buf)); \
+ int ret = snprintf(buf, nitems(buf), format, \
+ __VA_ARGS__); \
+ ATF_CHECK_EQ(strlen(output), ret); \
+ if (ret > 0) { \
+ ATF_CHECK_EQ(0, strcmp(output, buf)); \
+ } \
+ } while (0)
+
+ATF_TC_WITHOUT_HEAD(snprintf_b);
+ATF_TC_BODY(snprintf_b, tc)
+{
+ SNPRINTF_TEST("0", "%b", 0);
+ SNPRINTF_TEST(" 0", "%12b", 0);
+ SNPRINTF_TEST("000000000000", "%012b", 0);
+ SNPRINTF_TEST("1", "%b", 1);
+ SNPRINTF_TEST(" 1", "%12b", 1);
+ SNPRINTF_TEST("000000000001", "%012b", 1);
+ SNPRINTF_TEST("1111111111111111111111111111111", "%b", INT_MAX);
+ SNPRINTF_TEST("0", "%#b", 0);
+ SNPRINTF_TEST(" 0", "%#12b", 0);
+ SNPRINTF_TEST("000000000000", "%#012b", 0);
+ SNPRINTF_TEST("0b1", "%#b", 1);
+ SNPRINTF_TEST(" 0b1", "%#12b", 1);
+ SNPRINTF_TEST("0b0000000001", "%#012b", 1);
+ SNPRINTF_TEST("0b1111111111111111111111111111111", "%#b", INT_MAX);
+}
+
+ATF_TC_WITHOUT_HEAD(snprintf_B);
+ATF_TC_BODY(snprintf_B, tc)
+{
+ SNPRINTF_TEST("0", "%B", 0);
+ SNPRINTF_TEST(" 0", "%12B", 0);
+ SNPRINTF_TEST("000000000000", "%012B", 0);
+ SNPRINTF_TEST("1", "%B", 1);
+ SNPRINTF_TEST(" 1", "%12B", 1);
+ SNPRINTF_TEST("000000000001", "%012B", 1);
+ SNPRINTF_TEST("1111111111111111111111111111111", "%B", INT_MAX);
+ SNPRINTF_TEST("0", "%#B", 0);
+ SNPRINTF_TEST(" 0", "%#12B", 0);
+ SNPRINTF_TEST("000000000000", "%#012B", 0);
+ SNPRINTF_TEST("0B1", "%#B", 1);
+ SNPRINTF_TEST(" 0B1", "%#12B", 1);
+ SNPRINTF_TEST("0B0000000001", "%#012B", 1);
+ SNPRINTF_TEST("0B1111111111111111111111111111111", "%#B", INT_MAX);
+}
+
+ATF_TC_WITHOUT_HEAD(snprintf_d);
+ATF_TC_BODY(snprintf_d, tc)
+{
+ SNPRINTF_TEST("0", "%d", 0);
+ SNPRINTF_TEST(" 0", "%12d", 0);
+ SNPRINTF_TEST("000000000000", "%012d", 0);
+ SNPRINTF_TEST("1", "%d", 1);
+ SNPRINTF_TEST(" 1", "%12d", 1);
+ SNPRINTF_TEST("000000000001", "%012d", 1);
+ SNPRINTF_TEST("2147483647", "%d", INT_MAX);
+ SNPRINTF_TEST(" 2147483647", "%12d", INT_MAX);
+ SNPRINTF_TEST("002147483647", "%012d", INT_MAX);
+ SNPRINTF_TEST("2,147,483,647", "%'d", INT_MAX);
+}
+
+ATF_TC_WITHOUT_HEAD(snprintf_x);
+ATF_TC_BODY(snprintf_x, tc)
+{
+ SNPRINTF_TEST("0", "%x", 0);
+ SNPRINTF_TEST(" 0", "%12x", 0);
+ SNPRINTF_TEST("000000000000", "%012x", 0);
+ SNPRINTF_TEST("1", "%x", 1);
+ SNPRINTF_TEST(" 1", "%12x", 1);
+ SNPRINTF_TEST("000000000001", "%012x", 1);
+ SNPRINTF_TEST("7fffffff", "%x", INT_MAX);
+ SNPRINTF_TEST(" 7fffffff", "%12x", INT_MAX);
+ SNPRINTF_TEST("00007fffffff", "%012x", INT_MAX);
+ SNPRINTF_TEST("0", "%#x", 0);
+ SNPRINTF_TEST(" 0", "%#12x", 0);
+ SNPRINTF_TEST("000000000000", "%#012x", 0);
+ SNPRINTF_TEST("0x1", "%#x", 1);
+ SNPRINTF_TEST(" 0x1", "%#12x", 1);
+ SNPRINTF_TEST("0x0000000001", "%#012x", 1);
+ SNPRINTF_TEST("0x7fffffff", "%#x", INT_MAX);
+ SNPRINTF_TEST(" 0x7fffffff", "%#12x", INT_MAX);
+ SNPRINTF_TEST("0x007fffffff", "%#012x", INT_MAX);
+}
+
+ATF_TC_WITHOUT_HEAD(snprintf_X);
+ATF_TC_BODY(snprintf_X, tc)
+{
+ SNPRINTF_TEST("0", "%X", 0);
+ SNPRINTF_TEST(" 0", "%12X", 0);
+ SNPRINTF_TEST("000000000000", "%012X", 0);
+ SNPRINTF_TEST("1", "%X", 1);
+ SNPRINTF_TEST(" 1", "%12X", 1);
+ SNPRINTF_TEST("000000000001", "%012X", 1);
+ SNPRINTF_TEST("7FFFFFFF", "%X", INT_MAX);
+ SNPRINTF_TEST(" 7FFFFFFF", "%12X", INT_MAX);
+ SNPRINTF_TEST("00007FFFFFFF", "%012X", INT_MAX);
+ SNPRINTF_TEST("0", "%#X", 0);
+ SNPRINTF_TEST(" 0", "%#12X", 0);
+ SNPRINTF_TEST("000000000000", "%#012X", 0);
+ SNPRINTF_TEST("0X1", "%#X", 1);
+ SNPRINTF_TEST(" 0X1", "%#12X", 1);
+ SNPRINTF_TEST("0X0000000001", "%#012X", 1);
+ SNPRINTF_TEST("0X7FFFFFFF", "%#X", INT_MAX);
+ SNPRINTF_TEST(" 0X7FFFFFFF", "%#12X", INT_MAX);
+ SNPRINTF_TEST("0X007FFFFFFF", "%#012X", INT_MAX);
+}
+
+ATF_TC_WITHOUT_HEAD(snprintf_wN);
+ATF_TC_BODY(snprintf_wN, tc)
+{
+ SNPRINTF_TEST("0", "%w8d", (int8_t)0);
+ SNPRINTF_TEST("-128", "%w8d", (int8_t)SCHAR_MIN);
+ SNPRINTF_TEST("127", "%w8d", (int8_t)SCHAR_MAX);
+ SNPRINTF_TEST("0", "%w8u", (uint8_t)0);
+ SNPRINTF_TEST("255", "%w8u", (uint8_t)UCHAR_MAX);
+
+ SNPRINTF_TEST("0", "%w16d", (int16_t)0);
+ SNPRINTF_TEST("-32768", "%w16d", (int16_t)SHRT_MIN);
+ SNPRINTF_TEST("32767", "%w16d", (int16_t)SHRT_MAX);
+ SNPRINTF_TEST("0", "%w16u", (uint16_t)0);
+ SNPRINTF_TEST("65535", "%w16u", (uint16_t)USHRT_MAX);
+
+ SNPRINTF_TEST("0", "%w32d", (int32_t)0);
+ SNPRINTF_TEST("-2147483648", "%w32d", (int32_t)INT_MIN);
+ SNPRINTF_TEST("2147483647", "%w32d", (int32_t)INT_MAX);
+ SNPRINTF_TEST("0", "%w32u", (uint32_t)0);
+ SNPRINTF_TEST("4294967295", "%w32u", (uint32_t)UINT_MAX);
+
+ SNPRINTF_TEST("0", "%w64d", (int64_t)0);
+ SNPRINTF_TEST("-9223372036854775808", "%w64d", (int64_t)LLONG_MIN);
+ SNPRINTF_TEST("9223372036854775807", "%w64d", (int64_t)LLONG_MAX);
+ SNPRINTF_TEST("0", "%w64u", (uint64_t)0);
+ SNPRINTF_TEST("18446744073709551615", "%w64u", (uint64_t)ULLONG_MAX);
+
+ SNPRINTF_TEST("wd", "%wd", 0);
+ SNPRINTF_TEST("w1d", "%w1d", 0);
+ SNPRINTF_TEST("w128d", "%w128d", 0);
+}
+
+ATF_TC_WITHOUT_HEAD(snprintf_wfN);
+ATF_TC_BODY(snprintf_wfN, tc)
+{
+ SNPRINTF_TEST("0", "%wf8d", (int_fast8_t)0);
+ SNPRINTF_TEST("-2147483648", "%wf8d", (int_fast8_t)INT_MIN);
+ SNPRINTF_TEST("2147483647", "%wf8d", (int_fast8_t)INT_MAX);
+ SNPRINTF_TEST("0", "%wf8u", (uint8_t)0);
+ SNPRINTF_TEST("4294967295", "%wf8u", (uint_fast8_t)UINT_MAX);
+
+ SNPRINTF_TEST("0", "%wf16d", (int_fast16_t)0);
+ SNPRINTF_TEST("-2147483648", "%wf16d", (int_fast16_t)INT_MIN);
+ SNPRINTF_TEST("2147483647", "%wf16d", (int_fast16_t)INT_MAX);
+ SNPRINTF_TEST("0", "%wf16u", (uint16_t)0);
+ SNPRINTF_TEST("4294967295", "%wf16u", (uint_fast16_t)UINT_MAX);
+
+ SNPRINTF_TEST("0", "%wf32d", (int_fast32_t)0);
+ SNPRINTF_TEST("-2147483648", "%wf32d", (int_fast32_t)INT_MIN);
+ SNPRINTF_TEST("2147483647", "%wf32d", (int_fast32_t)INT_MAX);
+ SNPRINTF_TEST("0", "%wf32u", (uint32_t)0);
+ SNPRINTF_TEST("4294967295", "%wf32u", (uint_fast32_t)UINT_MAX);
+
+ SNPRINTF_TEST("0", "%wf64d", (int_fast64_t)0);
+ SNPRINTF_TEST("-9223372036854775808", "%wf64d", (int_fast64_t)LLONG_MIN);
+ SNPRINTF_TEST("9223372036854775807", "%wf64d", (int_fast64_t)LLONG_MAX);
+ SNPRINTF_TEST("0", "%wf64u", (uint64_t)0);
+ SNPRINTF_TEST("18446744073709551615", "%wf64u", (uint_fast64_t)ULLONG_MAX);
+
+ SNPRINTF_TEST("wfd", "%wfd", 0);
+ SNPRINTF_TEST("wf1d", "%wf1d", 0);
+ SNPRINTF_TEST("wf128d", "%wf128d", 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ setlocale(LC_NUMERIC, "en_US.UTF-8");
+ ATF_TP_ADD_TC(tp, snprintf_b);
+ ATF_TP_ADD_TC(tp, snprintf_B);
+ ATF_TP_ADD_TC(tp, snprintf_d);
+ ATF_TP_ADD_TC(tp, snprintf_x);
+ ATF_TP_ADD_TC(tp, snprintf_X);
+ ATF_TP_ADD_TC(tp, snprintf_wN);
+ ATF_TP_ADD_TC(tp, snprintf_wfN);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdio/sscanf_test.c b/lib/libc/tests/stdio/sscanf_test.c
new file mode 100644
index 000000000000..e43292820eeb
--- /dev/null
+++ b/lib/libc/tests/stdio/sscanf_test.c
@@ -0,0 +1,356 @@
+/*-
+ * Copyright (c) 2023 Dag-Erling Smørgrav
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <limits.h>
+#include <locale.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <atf-c.h>
+
+static const struct sscanf_test_case {
+ char input[8];
+ struct {
+ int ret, val, len;
+ } b, o, d, x, i;
+} sscanf_test_cases[] = {
+// input binary octal decimal hexadecimal automatic
+ // all digits
+ { "0", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
+ { "1", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, },
+ { "2", { 0, 0, 0 }, { 1, 2, 1 }, { 1, 2, 1 }, { 1, 2, 1 }, { 1, 2, 1 }, },
+ { "3", { 0, 0, 0 }, { 1, 3, 1 }, { 1, 3, 1 }, { 1, 3, 1 }, { 1, 3, 1 }, },
+ { "4", { 0, 0, 0 }, { 1, 4, 1 }, { 1, 4, 1 }, { 1, 4, 1 }, { 1, 4, 1 }, },
+ { "5", { 0, 0, 0 }, { 1, 5, 1 }, { 1, 5, 1 }, { 1, 5, 1 }, { 1, 5, 1 }, },
+ { "6", { 0, 0, 0 }, { 1, 6, 1 }, { 1, 6, 1 }, { 1, 6, 1 }, { 1, 6, 1 }, },
+ { "7", { 0, 0, 0 }, { 1, 7, 1 }, { 1, 7, 1 }, { 1, 7, 1 }, { 1, 7, 1 }, },
+ { "8", { 0, 0, 0 }, { 0, 0, 0 }, { 1, 8, 1 }, { 1, 8, 1 }, { 1, 8, 1 }, },
+ { "9", { 0, 0, 0 }, { 0, 0, 0 }, { 1, 9, 1 }, { 1, 9, 1 }, { 1, 9, 1 }, },
+ { "A", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 10, 1 }, { 0, 0, 0 }, },
+ { "B", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 11, 1 }, { 0, 0, 0 }, },
+ { "C", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 12, 1 }, { 0, 0, 0 }, },
+ { "D", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 13, 1 }, { 0, 0, 0 }, },
+ { "E", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 14, 1 }, { 0, 0, 0 }, },
+ { "F", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 15, 1 }, { 0, 0, 0 }, },
+ { "X", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, },
+ { "a", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 10, 1 }, { 0, 0, 0 }, },
+ { "b", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 11, 1 }, { 0, 0, 0 }, },
+ { "c", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 12, 1 }, { 0, 0, 0 }, },
+ { "d", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 13, 1 }, { 0, 0, 0 }, },
+ { "e", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 14, 1 }, { 0, 0, 0 }, },
+ { "f", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 15, 1 }, { 0, 0, 0 }, },
+ { "x", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, },
+ // all digits with leading zero
+ { "00", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, },
+ { "01", { 1, 1, 2 }, { 1, 1, 2 }, { 1, 1, 2 }, { 1, 1, 2 }, { 1, 1, 2 }, },
+ { "02", { 1, 0, 1 }, { 1, 2, 2 }, { 1, 2, 2 }, { 1, 2, 2 }, { 1, 2, 2 }, },
+ { "03", { 1, 0, 1 }, { 1, 3, 2 }, { 1, 3, 2 }, { 1, 3, 2 }, { 1, 3, 2 }, },
+ { "04", { 1, 0, 1 }, { 1, 4, 2 }, { 1, 4, 2 }, { 1, 4, 2 }, { 1, 4, 2 }, },
+ { "05", { 1, 0, 1 }, { 1, 5, 2 }, { 1, 5, 2 }, { 1, 5, 2 }, { 1, 5, 2 }, },
+ { "06", { 1, 0, 1 }, { 1, 6, 2 }, { 1, 6, 2 }, { 1, 6, 2 }, { 1, 6, 2 }, },
+ { "07", { 1, 0, 1 }, { 1, 7, 2 }, { 1, 7, 2 }, { 1, 7, 2 }, { 1, 7, 2 }, },
+ { "08", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 8, 2 }, { 1, 8, 2 }, { 1, 0, 1 }, },
+ { "09", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 9, 2 }, { 1, 9, 2 }, { 1, 0, 1 }, },
+ { "0A", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 2 }, { 1, 0, 1 }, },
+ { "0B", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, },
+ { "0C", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 2 }, { 1, 0, 1 }, },
+ { "0D", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 2 }, { 1, 0, 1 }, },
+ { "0E", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 2 }, { 1, 0, 1 }, },
+ { "0F", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 2 }, { 1, 0, 1 }, },
+ { "0X", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
+ { "0a", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 2 }, { 1, 0, 1 }, },
+ { "0b", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, },
+ { "0c", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 2 }, { 1, 0, 1 }, },
+ { "0d", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 2 }, { 1, 0, 1 }, },
+ { "0e", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 2 }, { 1, 0, 1 }, },
+ { "0f", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 2 }, { 1, 0, 1 }, },
+ { "0x", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
+ // all digits with two leading zeroes
+ { "000", { 1, 0, 3 }, { 1, 0, 3 }, { 1, 0, 3 }, { 1, 0, 3 }, { 1, 0, 3 }, },
+ { "001", { 1, 1, 3 }, { 1, 1, 3 }, { 1, 1, 3 }, { 1, 1, 3 }, { 1, 1, 3 }, },
+ { "002", { 1, 0, 2 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, },
+ { "003", { 1, 0, 2 }, { 1, 3, 3 }, { 1, 3, 3 }, { 1, 3, 3 }, { 1, 3, 3 }, },
+ { "004", { 1, 0, 2 }, { 1, 4, 3 }, { 1, 4, 3 }, { 1, 4, 3 }, { 1, 4, 3 }, },
+ { "005", { 1, 0, 2 }, { 1, 5, 3 }, { 1, 5, 3 }, { 1, 5, 3 }, { 1, 5, 3 }, },
+ { "006", { 1, 0, 2 }, { 1, 6, 3 }, { 1, 6, 3 }, { 1, 6, 3 }, { 1, 6, 3 }, },
+ { "007", { 1, 0, 2 }, { 1, 7, 3 }, { 1, 7, 3 }, { 1, 7, 3 }, { 1, 7, 3 }, },
+ { "008", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 8, 3 }, { 1, 8, 3 }, { 1, 0, 2 }, },
+ { "009", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 9, 3 }, { 1, 9, 3 }, { 1, 0, 2 }, },
+ { "00A", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 10, 3 }, { 1, 0, 2 }, },
+ { "00B", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 11, 3 }, { 1, 0, 2 }, },
+ { "00C", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 12, 3 }, { 1, 0, 2 }, },
+ { "00D", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 13, 3 }, { 1, 0, 2 }, },
+ { "00E", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 14, 3 }, { 1, 0, 2 }, },
+ { "00F", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 15, 3 }, { 1, 0, 2 }, },
+ { "00X", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, },
+ { "00a", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 10, 3 }, { 1, 0, 2 }, },
+ { "00b", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 11, 3 }, { 1, 0, 2 }, },
+ { "00c", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 12, 3 }, { 1, 0, 2 }, },
+ { "00d", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 13, 3 }, { 1, 0, 2 }, },
+ { "00e", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 14, 3 }, { 1, 0, 2 }, },
+ { "00f", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 15, 3 }, { 1, 0, 2 }, },
+ { "00x", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, },
+ // all digits with leading one
+ { "10", { 1, 2, 2 }, { 1, 8, 2 }, { 1, 10, 2 }, { 1, 16, 2 }, { 1, 10, 2 }, },
+ { "11", { 1, 3, 2 }, { 1, 9, 2 }, { 1, 11, 2 }, { 1, 17, 2 }, { 1, 11, 2 }, },
+ { "12", { 1, 1, 1 }, { 1, 10, 2 }, { 1, 12, 2 }, { 1, 18, 2 }, { 1, 12, 2 }, },
+ { "13", { 1, 1, 1 }, { 1, 11, 2 }, { 1, 13, 2 }, { 1, 19, 2 }, { 1, 13, 2 }, },
+ { "14", { 1, 1, 1 }, { 1, 12, 2 }, { 1, 14, 2 }, { 1, 20, 2 }, { 1, 14, 2 }, },
+ { "15", { 1, 1, 1 }, { 1, 13, 2 }, { 1, 15, 2 }, { 1, 21, 2 }, { 1, 15, 2 }, },
+ { "16", { 1, 1, 1 }, { 1, 14, 2 }, { 1, 16, 2 }, { 1, 22, 2 }, { 1, 16, 2 }, },
+ { "17", { 1, 1, 1 }, { 1, 15, 2 }, { 1, 17, 2 }, { 1, 23, 2 }, { 1, 17, 2 }, },
+ { "18", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 18, 2 }, { 1, 24, 2 }, { 1, 18, 2 }, },
+ { "19", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 19, 2 }, { 1, 25, 2 }, { 1, 19, 2 }, },
+ { "1A", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 26, 2 }, { 1, 1, 1 }, },
+ { "1B", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 27, 2 }, { 1, 1, 1 }, },
+ { "1C", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 28, 2 }, { 1, 1, 1 }, },
+ { "1D", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 29, 2 }, { 1, 1, 1 }, },
+ { "1E", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 30, 2 }, { 1, 1, 1 }, },
+ { "1F", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 31, 2 }, { 1, 1, 1 }, },
+ { "1X", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, },
+ { "1a", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 26, 2 }, { 1, 1, 1 }, },
+ { "1b", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 27, 2 }, { 1, 1, 1 }, },
+ { "1c", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 28, 2 }, { 1, 1, 1 }, },
+ { "1d", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 29, 2 }, { 1, 1, 1 }, },
+ { "1e", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 30, 2 }, { 1, 1, 1 }, },
+ { "1f", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 31, 2 }, { 1, 1, 1 }, },
+ { "1x", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, },
+ // all digits with leading binary prefix
+ { "0b0", { 1, 0, 3 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 176, 3 }, { 1, 0, 3 }, },
+ { "0b1", { 1, 1, 3 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 177, 3 }, { 1, 1, 3 }, },
+ { "0b2", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 178, 3 }, { 1, 0, 1 }, },
+ { "0b3", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 179, 3 }, { 1, 0, 1 }, },
+ { "0b4", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 180, 3 }, { 1, 0, 1 }, },
+ { "0b5", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 181, 3 }, { 1, 0, 1 }, },
+ { "0b6", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 182, 3 }, { 1, 0, 1 }, },
+ { "0b7", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 183, 3 }, { 1, 0, 1 }, },
+ { "0b8", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 184, 3 }, { 1, 0, 1 }, },
+ { "0b9", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 185, 3 }, { 1, 0, 1 }, },
+ { "0bA", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 186, 3 }, { 1, 0, 1 }, },
+ { "0bB", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 187, 3 }, { 1, 0, 1 }, },
+ { "0bC", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 188, 3 }, { 1, 0, 1 }, },
+ { "0bD", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 189, 3 }, { 1, 0, 1 }, },
+ { "0bE", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 190, 3 }, { 1, 0, 1 }, },
+ { "0bF", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 191, 3 }, { 1, 0, 1 }, },
+ { "0bX", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, },
+ { "0ba", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 186, 3 }, { 1, 0, 1 }, },
+ { "0bb", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 187, 3 }, { 1, 0, 1 }, },
+ { "0bc", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 188, 3 }, { 1, 0, 1 }, },
+ { "0bd", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 189, 3 }, { 1, 0, 1 }, },
+ { "0be", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 190, 3 }, { 1, 0, 1 }, },
+ { "0bf", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 191, 3 }, { 1, 0, 1 }, },
+ { "0bx", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, },
+ // all digits with leading hexadecimal prefix
+ { "0x0", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 3 }, { 1, 0, 3 }, },
+ { "0x1", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 1, 3 }, { 1, 1, 3 }, },
+ { "0x2", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 2, 3 }, { 1, 2, 3 }, },
+ { "0x3", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 3, 3 }, { 1, 3, 3 }, },
+ { "0x4", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 4, 3 }, { 1, 4, 3 }, },
+ { "0x5", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 5, 3 }, { 1, 5, 3 }, },
+ { "0x6", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 6, 3 }, { 1, 6, 3 }, },
+ { "0x7", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 7, 3 }, { 1, 7, 3 }, },
+ { "0x8", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 8, 3 }, { 1, 8, 3 }, },
+ { "0x9", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 9, 3 }, { 1, 9, 3 }, },
+ { "0xA", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 3 }, { 1, 10, 3 }, },
+ { "0xB", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 3 }, { 1, 11, 3 }, },
+ { "0xC", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 3 }, { 1, 12, 3 }, },
+ { "0xD", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 3 }, { 1, 13, 3 }, },
+ { "0xE", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 3 }, { 1, 14, 3 }, },
+ { "0xF", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 3 }, { 1, 15, 3 }, },
+ { "0xX", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
+ { "0xa", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 3 }, { 1, 10, 3 }, },
+ { "0xb", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 3 }, { 1, 11, 3 }, },
+ { "0xc", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 3 }, { 1, 12, 3 }, },
+ { "0xd", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 3 }, { 1, 13, 3 }, },
+ { "0xe", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 3 }, { 1, 14, 3 }, },
+ { "0xf", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 3 }, { 1, 15, 3 }, },
+ { "0xX", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
+ // terminator
+ { "" }
+};
+
+#define SSCANF_TEST(string, format, expret, expval, explen) \
+ do { \
+ int ret = 0, val = 0, len = 0; \
+ ret = sscanf(string, format "%n", &val, &len); \
+ ATF_CHECK_EQ(expret, ret); \
+ if (expret && ret) { \
+ ATF_CHECK_EQ(expval, val); \
+ ATF_CHECK_EQ(explen, len); \
+ } \
+ } while (0)
+
+ATF_TC_WITHOUT_HEAD(sscanf_b);
+ATF_TC_BODY(sscanf_b, tc)
+{
+ const struct sscanf_test_case *stc;
+ char input[16];
+
+ for (stc = sscanf_test_cases; *stc->input; stc++) {
+ strcpy(input + 1, stc->input);
+ SSCANF_TEST(input + 1, "%b", stc->b.ret, stc->b.val, stc->b.len);
+ input[0] = '+';
+ SSCANF_TEST(input, "%b", stc->b.ret, stc->b.val, stc->b.len ? stc->b.len + 1 : 0);
+ input[0] = '-';
+ SSCANF_TEST(input, "%b", stc->b.ret, -stc->b.val, stc->b.len ? stc->b.len + 1 : 0);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(sscanf_o);
+ATF_TC_BODY(sscanf_o, tc)
+{
+ const struct sscanf_test_case *stc;
+ char input[16];
+
+ for (stc = sscanf_test_cases; *stc->input; stc++) {
+ strcpy(input + 1, stc->input);
+ SSCANF_TEST(input + 1, "%o", stc->o.ret, stc->o.val, stc->o.len);
+ input[0] = '+';
+ SSCANF_TEST(input, "%o", stc->o.ret, stc->o.val, stc->o.len ? stc->o.len + 1 : 0);
+ input[0] = '-';
+ SSCANF_TEST(input, "%o", stc->o.ret, -stc->o.val, stc->o.len ? stc->o.len + 1 : 0);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(sscanf_d);
+ATF_TC_BODY(sscanf_d, tc)
+{
+ const struct sscanf_test_case *stc;
+ char input[16];
+
+ for (stc = sscanf_test_cases; *stc->input; stc++) {
+ strcpy(input + 1, stc->input);
+ SSCANF_TEST(input + 1, "%d", stc->d.ret, stc->d.val, stc->d.len);
+ input[0] = '+';
+ SSCANF_TEST(input, "%d", stc->d.ret, stc->d.val, stc->d.len ? stc->d.len + 1 : 0);
+ input[0] = '-';
+ SSCANF_TEST(input, "%d", stc->d.ret, -stc->d.val, stc->d.len ? stc->d.len + 1 : 0);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(sscanf_x);
+ATF_TC_BODY(sscanf_x, tc)
+{
+ const struct sscanf_test_case *stc;
+ char input[16];
+
+ for (stc = sscanf_test_cases; *stc->input; stc++) {
+ strcpy(input + 1, stc->input);
+ SSCANF_TEST(input + 1, "%x", stc->x.ret, stc->x.val, stc->x.len);
+ input[0] = '+';
+ SSCANF_TEST(input, "%x", stc->x.ret, stc->x.val, stc->x.len ? stc->x.len + 1 : 0);
+ input[0] = '-';
+ SSCANF_TEST(input, "%x", stc->x.ret, -stc->x.val, stc->x.len ? stc->x.len + 1 : 0);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(sscanf_i);
+ATF_TC_BODY(sscanf_i, tc)
+{
+ const struct sscanf_test_case *stc;
+ char input[16];
+
+ for (stc = sscanf_test_cases; *stc->input; stc++) {
+ strcpy(input + 1, stc->input);
+ SSCANF_TEST(input + 1, "%i", stc->i.ret, stc->i.val, stc->i.len);
+ input[0] = '+';
+ SSCANF_TEST(input, "%i", stc->i.ret, stc->i.val, stc->i.len ? stc->i.len + 1 : 0);
+ input[0] = '-';
+ SSCANF_TEST(input, "%i", stc->i.ret, -stc->i.val, stc->i.len ? stc->i.len + 1 : 0);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(sscanf_wN);
+ATF_TC_BODY(sscanf_wN, tc)
+{
+ const char x00[] = "0x00";
+ const char x7f[] = "0x7fffffffffffffff";
+ const char xff[] = "0xffffffffffffffff";
+
+#define SSCANF_WN_TEST(N, imin, umax) \
+ do { \
+ int##N##_t i; \
+ uint##N##_t u; \
+ ATF_CHECK_EQ(1, sscanf(x00, "%w" #N "i", &i)); \
+ ATF_CHECK_EQ(0, i); \
+ ATF_CHECK_EQ(1, sscanf(x7f, "%w" #N "i", &i)); \
+ ATF_CHECK_EQ(imin, i); \
+ ATF_CHECK_EQ(1, sscanf(x00, "%w" #N "x", &u)); \
+ ATF_CHECK_EQ(0, u); \
+ ATF_CHECK_EQ(1, sscanf(xff, "%w" #N "x", &u)); \
+ ATF_CHECK_EQ(umax, u); \
+ } while (0)
+ SSCANF_WN_TEST(8, -1, UCHAR_MAX);
+ SSCANF_WN_TEST(16, -1, USHRT_MAX);
+ SSCANF_WN_TEST(32, -1, UINT_MAX);
+ SSCANF_WN_TEST(64, LLONG_MAX, ULLONG_MAX);
+#undef SSCANF_WN_TEST
+
+ ATF_CHECK_EQ(0, sscanf(x00, "%wi", (int *)NULL));
+ ATF_CHECK_EQ(0, sscanf(x00, "%w1i", (int *)NULL));
+ ATF_CHECK_EQ(0, sscanf(x00, "%w128i", (int *)NULL));
+}
+
+ATF_TC_WITHOUT_HEAD(sscanf_wfN);
+ATF_TC_BODY(sscanf_wfN, tc)
+{
+ const char x00[] = "0x00";
+ const char x7f[] = "0x7fffffffffffffff";
+ const char xff[] = "0xffffffffffffffff";
+
+#define SSCANF_WFN_TEST(N, imin, umax) \
+ do { \
+ int_fast##N##_t i; \
+ uint_fast##N##_t u; \
+ ATF_CHECK_EQ(1, sscanf(x00, "%wf" #N "i", &i)); \
+ ATF_CHECK_EQ(0, i); \
+ ATF_CHECK_EQ(1, sscanf(x7f, "%wf" #N "i", &i)); \
+ ATF_CHECK_EQ(imin, i); \
+ ATF_CHECK_EQ(1, sscanf(x00, "%wf" #N "x", &u)); \
+ ATF_CHECK_EQ(0, u); \
+ ATF_CHECK_EQ(1, sscanf(xff, "%wf" #N "x", &u)); \
+ ATF_CHECK_EQ(umax, u); \
+ } while (0)
+ SSCANF_WFN_TEST(8, -1, UINT_MAX);
+ SSCANF_WFN_TEST(16, -1, UINT_MAX);
+ SSCANF_WFN_TEST(32, -1, UINT_MAX);
+ SSCANF_WFN_TEST(64, LLONG_MAX, ULLONG_MAX);
+#undef SSCANF_WFN_TEST
+
+ ATF_CHECK_EQ(0, sscanf(x00, "%wfi", (int *)NULL));
+ ATF_CHECK_EQ(0, sscanf(x00, "%wf1i", (int *)NULL));
+ ATF_CHECK_EQ(0, sscanf(x00, "%wf128i", (int *)NULL));
+}
+
+/*
+ * Test termination cases: non-numeric character, fixed width, EOF
+ */
+ATF_TC_WITHOUT_HEAD(sscanf_termination);
+ATF_TC_BODY(sscanf_termination, tc)
+{
+ int a = 0, b = 0, c = 0;
+ char d = 0;
+
+ ATF_CHECK_EQ(4, sscanf("3.1415", "%d%c%2d%d", &a, &d, &b, &c));
+ ATF_CHECK_EQ(3, a);
+ ATF_CHECK_EQ(14, b);
+ ATF_CHECK_EQ(15, c);
+ ATF_CHECK_EQ('.', d);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ setlocale(LC_NUMERIC, "en_US.UTF-8");
+ ATF_TP_ADD_TC(tp, sscanf_b);
+ ATF_TP_ADD_TC(tp, sscanf_o);
+ ATF_TP_ADD_TC(tp, sscanf_d);
+ ATF_TP_ADD_TC(tp, sscanf_x);
+ ATF_TP_ADD_TC(tp, sscanf_i);
+ ATF_TP_ADD_TC(tp, sscanf_wN);
+ ATF_TP_ADD_TC(tp, sscanf_wfN);
+ ATF_TP_ADD_TC(tp, sscanf_termination);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdio/swprintf_test.c b/lib/libc/tests/stdio/swprintf_test.c
new file mode 100644
index 000000000000..f00ecc7f1c1d
--- /dev/null
+++ b/lib/libc/tests/stdio/swprintf_test.c
@@ -0,0 +1,206 @@
+/*-
+ * Copyright (c) 2023 Dag-Erling Smørgrav
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <assert.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <wchar.h>
+
+#include <atf-c.h>
+
+#ifndef nitems
+#define nitems(a) (sizeof(a) / sizeof(a[0]))
+#endif
+
+#define SWPRINTF_TEST(output, format, ...) \
+ do { \
+ wchar_t buf[256]; \
+ assert(wcslen(L##output) < nitems(buf)); \
+ int ret = swprintf(buf, nitems(buf), L##format, \
+ __VA_ARGS__); \
+ ATF_CHECK_EQ(wcslen(L##output), ret); \
+ if (ret > 0) { \
+ ATF_CHECK_EQ(0, wcscmp(L##output, buf)); \
+ } \
+ } while (0)
+
+ATF_TC_WITHOUT_HEAD(swprintf_b);
+ATF_TC_BODY(swprintf_b, tc)
+{
+ SWPRINTF_TEST("0", "%b", 0);
+ SWPRINTF_TEST(" 0", "%12b", 0);
+ SWPRINTF_TEST("000000000000", "%012b", 0);
+ SWPRINTF_TEST("1", "%b", 1);
+ SWPRINTF_TEST(" 1", "%12b", 1);
+ SWPRINTF_TEST("000000000001", "%012b", 1);
+ SWPRINTF_TEST("1111111111111111111111111111111", "%b", INT_MAX);
+ SWPRINTF_TEST("0", "%#b", 0);
+ SWPRINTF_TEST(" 0", "%#12b", 0);
+ SWPRINTF_TEST("000000000000", "%#012b", 0);
+ SWPRINTF_TEST("0b1", "%#b", 1);
+ SWPRINTF_TEST(" 0b1", "%#12b", 1);
+ SWPRINTF_TEST("0b0000000001", "%#012b", 1);
+ SWPRINTF_TEST("0b1111111111111111111111111111111", "%#b", INT_MAX);
+}
+
+ATF_TC_WITHOUT_HEAD(swprintf_B);
+ATF_TC_BODY(swprintf_B, tc)
+{
+ SWPRINTF_TEST("0", "%B", 0);
+ SWPRINTF_TEST(" 0", "%12B", 0);
+ SWPRINTF_TEST("000000000000", "%012B", 0);
+ SWPRINTF_TEST("1", "%B", 1);
+ SWPRINTF_TEST(" 1", "%12B", 1);
+ SWPRINTF_TEST("000000000001", "%012B", 1);
+ SWPRINTF_TEST("1111111111111111111111111111111", "%B", INT_MAX);
+ SWPRINTF_TEST("0", "%#B", 0);
+ SWPRINTF_TEST(" 0", "%#12B", 0);
+ SWPRINTF_TEST("000000000000", "%#012B", 0);
+ SWPRINTF_TEST("0B1", "%#B", 1);
+ SWPRINTF_TEST(" 0B1", "%#12B", 1);
+ SWPRINTF_TEST("0B0000000001", "%#012B", 1);
+ SWPRINTF_TEST("0B1111111111111111111111111111111", "%#B", INT_MAX);
+}
+
+ATF_TC_WITHOUT_HEAD(swprintf_d);
+ATF_TC_BODY(swprintf_d, tc)
+{
+ SWPRINTF_TEST("0", "%d", 0);
+ SWPRINTF_TEST(" 0", "%12d", 0);
+ SWPRINTF_TEST("000000000000", "%012d", 0);
+ SWPRINTF_TEST("1", "%d", 1);
+ SWPRINTF_TEST(" 1", "%12d", 1);
+ SWPRINTF_TEST("000000000001", "%012d", 1);
+ SWPRINTF_TEST("2147483647", "%d", INT_MAX);
+ SWPRINTF_TEST(" 2147483647", "%12d", INT_MAX);
+ SWPRINTF_TEST("002147483647", "%012d", INT_MAX);
+ SWPRINTF_TEST("2,147,483,647", "%'d", INT_MAX);
+}
+
+ATF_TC_WITHOUT_HEAD(swprintf_x);
+ATF_TC_BODY(swprintf_x, tc)
+{
+ SWPRINTF_TEST("0", "%x", 0);
+ SWPRINTF_TEST(" 0", "%12x", 0);
+ SWPRINTF_TEST("000000000000", "%012x", 0);
+ SWPRINTF_TEST("1", "%x", 1);
+ SWPRINTF_TEST(" 1", "%12x", 1);
+ SWPRINTF_TEST("000000000001", "%012x", 1);
+ SWPRINTF_TEST("7fffffff", "%x", INT_MAX);
+ SWPRINTF_TEST(" 7fffffff", "%12x", INT_MAX);
+ SWPRINTF_TEST("00007fffffff", "%012x", INT_MAX);
+ SWPRINTF_TEST("0", "%#x", 0);
+ SWPRINTF_TEST(" 0", "%#12x", 0);
+ SWPRINTF_TEST("000000000000", "%#012x", 0);
+ SWPRINTF_TEST("0x1", "%#x", 1);
+ SWPRINTF_TEST(" 0x1", "%#12x", 1);
+ SWPRINTF_TEST("0x0000000001", "%#012x", 1);
+ SWPRINTF_TEST("0x7fffffff", "%#x", INT_MAX);
+ SWPRINTF_TEST(" 0x7fffffff", "%#12x", INT_MAX);
+ SWPRINTF_TEST("0x007fffffff", "%#012x", INT_MAX);
+}
+
+ATF_TC_WITHOUT_HEAD(swprintf_X);
+ATF_TC_BODY(swprintf_X, tc)
+{
+ SWPRINTF_TEST("0", "%X", 0);
+ SWPRINTF_TEST(" 0", "%12X", 0);
+ SWPRINTF_TEST("000000000000", "%012X", 0);
+ SWPRINTF_TEST("1", "%X", 1);
+ SWPRINTF_TEST(" 1", "%12X", 1);
+ SWPRINTF_TEST("000000000001", "%012X", 1);
+ SWPRINTF_TEST("7FFFFFFF", "%X", INT_MAX);
+ SWPRINTF_TEST(" 7FFFFFFF", "%12X", INT_MAX);
+ SWPRINTF_TEST("00007FFFFFFF", "%012X", INT_MAX);
+ SWPRINTF_TEST("0", "%#X", 0);
+ SWPRINTF_TEST(" 0", "%#12X", 0);
+ SWPRINTF_TEST("000000000000", "%#012X", 0);
+ SWPRINTF_TEST("0X1", "%#X", 1);
+ SWPRINTF_TEST(" 0X1", "%#12X", 1);
+ SWPRINTF_TEST("0X0000000001", "%#012X", 1);
+ SWPRINTF_TEST("0X7FFFFFFF", "%#X", INT_MAX);
+ SWPRINTF_TEST(" 0X7FFFFFFF", "%#12X", INT_MAX);
+ SWPRINTF_TEST("0X007FFFFFFF", "%#012X", INT_MAX);
+}
+
+ATF_TC_WITHOUT_HEAD(swprintf_wN);
+ATF_TC_BODY(swprintf_wN, tc)
+{
+ SWPRINTF_TEST("0", "%w8d", (int8_t)0);
+ SWPRINTF_TEST("-128", "%w8d", (int8_t)SCHAR_MIN);
+ SWPRINTF_TEST("127", "%w8d", (int8_t)SCHAR_MAX);
+ SWPRINTF_TEST("0", "%w8u", (uint8_t)0);
+ SWPRINTF_TEST("255", "%w8u", (uint8_t)UCHAR_MAX);
+
+ SWPRINTF_TEST("0", "%w16d", (int16_t)0);
+ SWPRINTF_TEST("-32768", "%w16d", (int16_t)SHRT_MIN);
+ SWPRINTF_TEST("32767", "%w16d", (int16_t)SHRT_MAX);
+ SWPRINTF_TEST("0", "%w16u", (uint16_t)0);
+ SWPRINTF_TEST("65535", "%w16u", (uint16_t)USHRT_MAX);
+
+ SWPRINTF_TEST("0", "%w32d", (int32_t)0);
+ SWPRINTF_TEST("-2147483648", "%w32d", (int32_t)INT_MIN);
+ SWPRINTF_TEST("2147483647", "%w32d", (int32_t)INT_MAX);
+ SWPRINTF_TEST("0", "%w32u", (uint32_t)0);
+ SWPRINTF_TEST("4294967295", "%w32u", (uint32_t)UINT_MAX);
+
+ SWPRINTF_TEST("0", "%w64d", (int64_t)0);
+ SWPRINTF_TEST("-9223372036854775808", "%w64d", (int64_t)LLONG_MIN);
+ SWPRINTF_TEST("9223372036854775807", "%w64d", (int64_t)LLONG_MAX);
+ SWPRINTF_TEST("0", "%w64u", (uint64_t)0);
+ SWPRINTF_TEST("18446744073709551615", "%w64u", (uint64_t)ULLONG_MAX);
+
+ SWPRINTF_TEST("wd", "%wd", 0);
+ SWPRINTF_TEST("w1d", "%w1d", 0);
+ SWPRINTF_TEST("w128d", "%w128d", 0);
+}
+
+ATF_TC_WITHOUT_HEAD(swprintf_wfN);
+ATF_TC_BODY(swprintf_wfN, tc)
+{
+ SWPRINTF_TEST("0", "%wf8d", (int_fast8_t)0);
+ SWPRINTF_TEST("-2147483648", "%wf8d", (int_fast8_t)INT_MIN);
+ SWPRINTF_TEST("2147483647", "%wf8d", (int_fast8_t)INT_MAX);
+ SWPRINTF_TEST("0", "%wf8u", (uint8_t)0);
+ SWPRINTF_TEST("4294967295", "%wf8u", (uint_fast8_t)UINT_MAX);
+
+ SWPRINTF_TEST("0", "%wf16d", (int_fast16_t)0);
+ SWPRINTF_TEST("-2147483648", "%wf16d", (int_fast16_t)INT_MIN);
+ SWPRINTF_TEST("2147483647", "%wf16d", (int_fast16_t)INT_MAX);
+ SWPRINTF_TEST("0", "%wf16u", (uint16_t)0);
+ SWPRINTF_TEST("4294967295", "%wf16u", (uint_fast16_t)UINT_MAX);
+
+ SWPRINTF_TEST("0", "%wf32d", (int_fast32_t)0);
+ SWPRINTF_TEST("-2147483648", "%wf32d", (int_fast32_t)INT_MIN);
+ SWPRINTF_TEST("2147483647", "%wf32d", (int_fast32_t)INT_MAX);
+ SWPRINTF_TEST("0", "%wf32u", (uint32_t)0);
+ SWPRINTF_TEST("4294967295", "%wf32u", (uint_fast32_t)UINT_MAX);
+
+ SWPRINTF_TEST("0", "%wf64d", (int_fast64_t)0);
+ SWPRINTF_TEST("-9223372036854775808", "%wf64d", (int_fast64_t)LLONG_MIN);
+ SWPRINTF_TEST("9223372036854775807", "%wf64d", (int_fast64_t)LLONG_MAX);
+ SWPRINTF_TEST("0", "%wf64u", (uint64_t)0);
+ SWPRINTF_TEST("18446744073709551615", "%wf64u", (uint_fast64_t)ULLONG_MAX);
+
+ SWPRINTF_TEST("wfd", "%wfd", 0);
+ SWPRINTF_TEST("wf1d", "%wf1d", 0);
+ SWPRINTF_TEST("wf128d", "%wf128d", 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ setlocale(LC_NUMERIC, "en_US.UTF-8");
+ ATF_TP_ADD_TC(tp, swprintf_b);
+ ATF_TP_ADD_TC(tp, swprintf_B);
+ ATF_TP_ADD_TC(tp, swprintf_d);
+ ATF_TP_ADD_TC(tp, swprintf_x);
+ ATF_TP_ADD_TC(tp, swprintf_X);
+ ATF_TP_ADD_TC(tp, swprintf_wN);
+ ATF_TP_ADD_TC(tp, swprintf_wfN);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdio/swscanf_test.c b/lib/libc/tests/stdio/swscanf_test.c
new file mode 100644
index 000000000000..f0638081e16f
--- /dev/null
+++ b/lib/libc/tests/stdio/swscanf_test.c
@@ -0,0 +1,359 @@
+/*-
+ * Copyright (c) 2023 Dag-Erling Smørgrav
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <limits.h>
+#include <locale.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <wchar.h>
+
+#include <atf-c.h>
+
+#define L(s) L ## s
+
+static const struct swscanf_test_case {
+ wchar_t input[8];
+ struct {
+ int ret, val, len;
+ } b, o, d, x, i;
+} swscanf_test_cases[] = {
+// input binary octal decimal hexadecimal automatic
+ // all digits
+ { L"0", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
+ { L"1", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, },
+ { L"2", { 0, 0, 0 }, { 1, 2, 1 }, { 1, 2, 1 }, { 1, 2, 1 }, { 1, 2, 1 }, },
+ { L"3", { 0, 0, 0 }, { 1, 3, 1 }, { 1, 3, 1 }, { 1, 3, 1 }, { 1, 3, 1 }, },
+ { L"4", { 0, 0, 0 }, { 1, 4, 1 }, { 1, 4, 1 }, { 1, 4, 1 }, { 1, 4, 1 }, },
+ { L"5", { 0, 0, 0 }, { 1, 5, 1 }, { 1, 5, 1 }, { 1, 5, 1 }, { 1, 5, 1 }, },
+ { L"6", { 0, 0, 0 }, { 1, 6, 1 }, { 1, 6, 1 }, { 1, 6, 1 }, { 1, 6, 1 }, },
+ { L"7", { 0, 0, 0 }, { 1, 7, 1 }, { 1, 7, 1 }, { 1, 7, 1 }, { 1, 7, 1 }, },
+ { L"8", { 0, 0, 0 }, { 0, 0, 0 }, { 1, 8, 1 }, { 1, 8, 1 }, { 1, 8, 1 }, },
+ { L"9", { 0, 0, 0 }, { 0, 0, 0 }, { 1, 9, 1 }, { 1, 9, 1 }, { 1, 9, 1 }, },
+ { L"A", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 10, 1 }, { 0, 0, 0 }, },
+ { L"B", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 11, 1 }, { 0, 0, 0 }, },
+ { L"C", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 12, 1 }, { 0, 0, 0 }, },
+ { L"D", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 13, 1 }, { 0, 0, 0 }, },
+ { L"E", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 14, 1 }, { 0, 0, 0 }, },
+ { L"F", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 15, 1 }, { 0, 0, 0 }, },
+ { L"X", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, },
+ { L"a", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 10, 1 }, { 0, 0, 0 }, },
+ { L"b", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 11, 1 }, { 0, 0, 0 }, },
+ { L"c", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 12, 1 }, { 0, 0, 0 }, },
+ { L"d", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 13, 1 }, { 0, 0, 0 }, },
+ { L"e", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 14, 1 }, { 0, 0, 0 }, },
+ { L"f", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1, 15, 1 }, { 0, 0, 0 }, },
+ { L"x", { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, },
+ // all digits with leading zero
+ { L"00", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, },
+ { L"01", { 1, 1, 2 }, { 1, 1, 2 }, { 1, 1, 2 }, { 1, 1, 2 }, { 1, 1, 2 }, },
+ { L"02", { 1, 0, 1 }, { 1, 2, 2 }, { 1, 2, 2 }, { 1, 2, 2 }, { 1, 2, 2 }, },
+ { L"03", { 1, 0, 1 }, { 1, 3, 2 }, { 1, 3, 2 }, { 1, 3, 2 }, { 1, 3, 2 }, },
+ { L"04", { 1, 0, 1 }, { 1, 4, 2 }, { 1, 4, 2 }, { 1, 4, 2 }, { 1, 4, 2 }, },
+ { L"05", { 1, 0, 1 }, { 1, 5, 2 }, { 1, 5, 2 }, { 1, 5, 2 }, { 1, 5, 2 }, },
+ { L"06", { 1, 0, 1 }, { 1, 6, 2 }, { 1, 6, 2 }, { 1, 6, 2 }, { 1, 6, 2 }, },
+ { L"07", { 1, 0, 1 }, { 1, 7, 2 }, { 1, 7, 2 }, { 1, 7, 2 }, { 1, 7, 2 }, },
+ { L"08", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 8, 2 }, { 1, 8, 2 }, { 1, 0, 1 }, },
+ { L"09", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 9, 2 }, { 1, 9, 2 }, { 1, 0, 1 }, },
+ { L"0A", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 2 }, { 1, 0, 1 }, },
+ { L"0B", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, },
+ { L"0C", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 2 }, { 1, 0, 1 }, },
+ { L"0D", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 2 }, { 1, 0, 1 }, },
+ { L"0E", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 2 }, { 1, 0, 1 }, },
+ { L"0F", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 2 }, { 1, 0, 1 }, },
+ { L"0X", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
+ { L"0a", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 2 }, { 1, 0, 1 }, },
+ { L"0b", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, },
+ { L"0c", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 2 }, { 1, 0, 1 }, },
+ { L"0d", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 2 }, { 1, 0, 1 }, },
+ { L"0e", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 2 }, { 1, 0, 1 }, },
+ { L"0f", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 2 }, { 1, 0, 1 }, },
+ { L"0x", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
+ // all digits with two leading zeroes
+ { L"000", { 1, 0, 3 }, { 1, 0, 3 }, { 1, 0, 3 }, { 1, 0, 3 }, { 1, 0, 3 }, },
+ { L"001", { 1, 1, 3 }, { 1, 1, 3 }, { 1, 1, 3 }, { 1, 1, 3 }, { 1, 1, 3 }, },
+ { L"002", { 1, 0, 2 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, { 1, 2, 3 }, },
+ { L"003", { 1, 0, 2 }, { 1, 3, 3 }, { 1, 3, 3 }, { 1, 3, 3 }, { 1, 3, 3 }, },
+ { L"004", { 1, 0, 2 }, { 1, 4, 3 }, { 1, 4, 3 }, { 1, 4, 3 }, { 1, 4, 3 }, },
+ { L"005", { 1, 0, 2 }, { 1, 5, 3 }, { 1, 5, 3 }, { 1, 5, 3 }, { 1, 5, 3 }, },
+ { L"006", { 1, 0, 2 }, { 1, 6, 3 }, { 1, 6, 3 }, { 1, 6, 3 }, { 1, 6, 3 }, },
+ { L"007", { 1, 0, 2 }, { 1, 7, 3 }, { 1, 7, 3 }, { 1, 7, 3 }, { 1, 7, 3 }, },
+ { L"008", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 8, 3 }, { 1, 8, 3 }, { 1, 0, 2 }, },
+ { L"009", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 9, 3 }, { 1, 9, 3 }, { 1, 0, 2 }, },
+ { L"00A", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 10, 3 }, { 1, 0, 2 }, },
+ { L"00B", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 11, 3 }, { 1, 0, 2 }, },
+ { L"00C", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 12, 3 }, { 1, 0, 2 }, },
+ { L"00D", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 13, 3 }, { 1, 0, 2 }, },
+ { L"00E", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 14, 3 }, { 1, 0, 2 }, },
+ { L"00F", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 15, 3 }, { 1, 0, 2 }, },
+ { L"00X", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, },
+ { L"00a", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 10, 3 }, { 1, 0, 2 }, },
+ { L"00b", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 11, 3 }, { 1, 0, 2 }, },
+ { L"00c", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 12, 3 }, { 1, 0, 2 }, },
+ { L"00d", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 13, 3 }, { 1, 0, 2 }, },
+ { L"00e", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 14, 3 }, { 1, 0, 2 }, },
+ { L"00f", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 15, 3 }, { 1, 0, 2 }, },
+ { L"00x", { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, { 1, 0, 2 }, },
+ // all digits with leading one
+ { L"10", { 1, 2, 2 }, { 1, 8, 2 }, { 1, 10, 2 }, { 1, 16, 2 }, { 1, 10, 2 }, },
+ { L"11", { 1, 3, 2 }, { 1, 9, 2 }, { 1, 11, 2 }, { 1, 17, 2 }, { 1, 11, 2 }, },
+ { L"12", { 1, 1, 1 }, { 1, 10, 2 }, { 1, 12, 2 }, { 1, 18, 2 }, { 1, 12, 2 }, },
+ { L"13", { 1, 1, 1 }, { 1, 11, 2 }, { 1, 13, 2 }, { 1, 19, 2 }, { 1, 13, 2 }, },
+ { L"14", { 1, 1, 1 }, { 1, 12, 2 }, { 1, 14, 2 }, { 1, 20, 2 }, { 1, 14, 2 }, },
+ { L"15", { 1, 1, 1 }, { 1, 13, 2 }, { 1, 15, 2 }, { 1, 21, 2 }, { 1, 15, 2 }, },
+ { L"16", { 1, 1, 1 }, { 1, 14, 2 }, { 1, 16, 2 }, { 1, 22, 2 }, { 1, 16, 2 }, },
+ { L"17", { 1, 1, 1 }, { 1, 15, 2 }, { 1, 17, 2 }, { 1, 23, 2 }, { 1, 17, 2 }, },
+ { L"18", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 18, 2 }, { 1, 24, 2 }, { 1, 18, 2 }, },
+ { L"19", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 19, 2 }, { 1, 25, 2 }, { 1, 19, 2 }, },
+ { L"1A", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 26, 2 }, { 1, 1, 1 }, },
+ { L"1B", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 27, 2 }, { 1, 1, 1 }, },
+ { L"1C", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 28, 2 }, { 1, 1, 1 }, },
+ { L"1D", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 29, 2 }, { 1, 1, 1 }, },
+ { L"1E", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 30, 2 }, { 1, 1, 1 }, },
+ { L"1F", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 31, 2 }, { 1, 1, 1 }, },
+ { L"1X", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, },
+ { L"1a", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 26, 2 }, { 1, 1, 1 }, },
+ { L"1b", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 27, 2 }, { 1, 1, 1 }, },
+ { L"1c", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 28, 2 }, { 1, 1, 1 }, },
+ { L"1d", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 29, 2 }, { 1, 1, 1 }, },
+ { L"1e", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 30, 2 }, { 1, 1, 1 }, },
+ { L"1f", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 31, 2 }, { 1, 1, 1 }, },
+ { L"1x", { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 }, },
+ // all digits with leading binary prefix
+ { L"0b0", { 1, 0, 3 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 176, 3 }, { 1, 0, 3 }, },
+ { L"0b1", { 1, 1, 3 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 177, 3 }, { 1, 1, 3 }, },
+ { L"0b2", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 178, 3 }, { 1, 0, 1 }, },
+ { L"0b3", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 179, 3 }, { 1, 0, 1 }, },
+ { L"0b4", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 180, 3 }, { 1, 0, 1 }, },
+ { L"0b5", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 181, 3 }, { 1, 0, 1 }, },
+ { L"0b6", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 182, 3 }, { 1, 0, 1 }, },
+ { L"0b7", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 183, 3 }, { 1, 0, 1 }, },
+ { L"0b8", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 184, 3 }, { 1, 0, 1 }, },
+ { L"0b9", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 185, 3 }, { 1, 0, 1 }, },
+ { L"0bA", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 186, 3 }, { 1, 0, 1 }, },
+ { L"0bB", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 187, 3 }, { 1, 0, 1 }, },
+ { L"0bC", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 188, 3 }, { 1, 0, 1 }, },
+ { L"0bD", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 189, 3 }, { 1, 0, 1 }, },
+ { L"0bE", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 190, 3 }, { 1, 0, 1 }, },
+ { L"0bF", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 191, 3 }, { 1, 0, 1 }, },
+ { L"0bX", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, },
+ { L"0ba", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 186, 3 }, { 1, 0, 1 }, },
+ { L"0bb", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 187, 3 }, { 1, 0, 1 }, },
+ { L"0bc", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 188, 3 }, { 1, 0, 1 }, },
+ { L"0bd", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 189, 3 }, { 1, 0, 1 }, },
+ { L"0be", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 190, 3 }, { 1, 0, 1 }, },
+ { L"0bf", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 191, 3 }, { 1, 0, 1 }, },
+ { L"0bx", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 2 }, { 1, 0, 1 }, },
+ // all digits with leading hexadecimal prefix
+ { L"0x0", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 3 }, { 1, 0, 3 }, },
+ { L"0x1", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 1, 3 }, { 1, 1, 3 }, },
+ { L"0x2", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 2, 3 }, { 1, 2, 3 }, },
+ { L"0x3", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 3, 3 }, { 1, 3, 3 }, },
+ { L"0x4", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 4, 3 }, { 1, 4, 3 }, },
+ { L"0x5", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 5, 3 }, { 1, 5, 3 }, },
+ { L"0x6", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 6, 3 }, { 1, 6, 3 }, },
+ { L"0x7", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 7, 3 }, { 1, 7, 3 }, },
+ { L"0x8", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 8, 3 }, { 1, 8, 3 }, },
+ { L"0x9", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 9, 3 }, { 1, 9, 3 }, },
+ { L"0xA", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 3 }, { 1, 10, 3 }, },
+ { L"0xB", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 3 }, { 1, 11, 3 }, },
+ { L"0xC", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 3 }, { 1, 12, 3 }, },
+ { L"0xD", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 3 }, { 1, 13, 3 }, },
+ { L"0xE", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 3 }, { 1, 14, 3 }, },
+ { L"0xF", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 3 }, { 1, 15, 3 }, },
+ { L"0xX", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
+ { L"0xa", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 10, 3 }, { 1, 10, 3 }, },
+ { L"0xb", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 11, 3 }, { 1, 11, 3 }, },
+ { L"0xc", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 12, 3 }, { 1, 12, 3 }, },
+ { L"0xd", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 13, 3 }, { 1, 13, 3 }, },
+ { L"0xe", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 14, 3 }, { 1, 14, 3 }, },
+ { L"0xf", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 15, 3 }, { 1, 15, 3 }, },
+ { L"0xX", { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, { 1, 0, 1 }, },
+ // terminator
+ { L"" }
+};
+
+#define SWSCANF_TEST(string, format, expret, expval, explen) \
+ do { \
+ int ret = 0, val = 0, len = 0; \
+ ret = swscanf(string, format L"%n", &val, &len); \
+ ATF_CHECK_EQ(expret, ret); \
+ if (expret && ret) { \
+ ATF_CHECK_EQ(expval, val); \
+ ATF_CHECK_EQ(explen, len); \
+ } \
+ } while (0)
+
+ATF_TC_WITHOUT_HEAD(swscanf_b);
+ATF_TC_BODY(swscanf_b, tc)
+{
+ const struct swscanf_test_case *stc;
+ wchar_t input[16];
+
+ for (stc = swscanf_test_cases; *stc->input; stc++) {
+ wcscpy(input + 1, stc->input);
+ SWSCANF_TEST(input + 1, L"%b", stc->b.ret, stc->b.val, stc->b.len);
+ input[0] = L'+';
+ SWSCANF_TEST(input, L"%b", stc->b.ret, stc->b.val, stc->b.len ? stc->b.len + 1 : 0);
+ input[0] = L'-';
+ SWSCANF_TEST(input, L"%b", stc->b.ret, -stc->b.val, stc->b.len ? stc->b.len + 1 : 0);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(swscanf_o);
+ATF_TC_BODY(swscanf_o, tc)
+{
+ const struct swscanf_test_case *stc;
+ wchar_t input[16];
+
+ for (stc = swscanf_test_cases; *stc->input; stc++) {
+ wcscpy(input + 1, stc->input);
+ SWSCANF_TEST(input + 1, L"%o", stc->o.ret, stc->o.val, stc->o.len);
+ input[0] = L'+';
+ SWSCANF_TEST(input, L"%o", stc->o.ret, stc->o.val, stc->o.len ? stc->o.len + 1 : 0);
+ input[0] = L'-';
+ SWSCANF_TEST(input, L"%o", stc->o.ret, -stc->o.val, stc->o.len ? stc->o.len + 1 : 0);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(swscanf_d);
+ATF_TC_BODY(swscanf_d, tc)
+{
+ const struct swscanf_test_case *stc;
+ wchar_t input[16];
+
+ for (stc = swscanf_test_cases; *stc->input; stc++) {
+ wcscpy(input + 1, stc->input);
+ SWSCANF_TEST(input + 1, L"%d", stc->d.ret, stc->d.val, stc->d.len);
+ input[0] = L'+';
+ SWSCANF_TEST(input, L"%d", stc->d.ret, stc->d.val, stc->d.len ? stc->d.len + 1 : 0);
+ input[0] = L'-';
+ SWSCANF_TEST(input, L"%d", stc->d.ret, -stc->d.val, stc->d.len ? stc->d.len + 1 : 0);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(swscanf_x);
+ATF_TC_BODY(swscanf_x, tc)
+{
+ const struct swscanf_test_case *stc;
+ wchar_t input[16];
+
+ for (stc = swscanf_test_cases; *stc->input; stc++) {
+ wcscpy(input + 1, stc->input);
+ SWSCANF_TEST(input + 1, L"%x", stc->x.ret, stc->x.val, stc->x.len);
+ input[0] = L'+';
+ SWSCANF_TEST(input, L"%x", stc->x.ret, stc->x.val, stc->x.len ? stc->x.len + 1 : 0);
+ input[0] = L'-';
+ SWSCANF_TEST(input, L"%x", stc->x.ret, -stc->x.val, stc->x.len ? stc->x.len + 1 : 0);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(swscanf_i);
+ATF_TC_BODY(swscanf_i, tc)
+{
+ const struct swscanf_test_case *stc;
+ wchar_t input[16];
+
+ for (stc = swscanf_test_cases; *stc->input; stc++) {
+ wcscpy(input + 1, stc->input);
+ SWSCANF_TEST(input + 1, L"%i", stc->i.ret, stc->i.val, stc->i.len);
+ input[0] = L'+';
+ SWSCANF_TEST(input, L"%i", stc->i.ret, stc->i.val, stc->i.len ? stc->i.len + 1 : 0);
+ input[0] = L'-';
+ SWSCANF_TEST(input, L"%i", stc->i.ret, -stc->i.val, stc->i.len ? stc->i.len + 1 : 0);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(swscanf_wN);
+ATF_TC_BODY(swscanf_wN, tc)
+{
+ const wchar_t x00[] = L"0x00";
+ const wchar_t x7f[] = L"0x7fffffffffffffff";
+ const wchar_t xff[] = L"0xffffffffffffffff";
+
+#define SWSCANF_WN_TEST(N, imin, umax) \
+ do { \
+ int##N##_t i; \
+ uint##N##_t u; \
+ ATF_CHECK_EQ(1, swscanf(x00, L"%w" L(#N) L"i", &i)); \
+ ATF_CHECK_EQ(0, i); \
+ ATF_CHECK_EQ(1, swscanf(x7f, L"%w" L(#N) L"i", &i)); \
+ ATF_CHECK_EQ(imin, i); \
+ ATF_CHECK_EQ(1, swscanf(x00, L"%w" L(#N) L"x", &u)); \
+ ATF_CHECK_EQ(0, u); \
+ ATF_CHECK_EQ(1, swscanf(xff, L"%w" L(#N) L"x", &u)); \
+ ATF_CHECK_EQ(umax, u); \
+ } while (0)
+ SWSCANF_WN_TEST(8, -1, UCHAR_MAX);
+ SWSCANF_WN_TEST(16, -1, USHRT_MAX);
+ SWSCANF_WN_TEST(32, -1, UINT_MAX);
+ SWSCANF_WN_TEST(64, LLONG_MAX, ULLONG_MAX);
+#undef SWSCANF_WN_TEST
+
+ ATF_CHECK_EQ(0, swscanf(x00, L"%wi", (int *)NULL));
+ ATF_CHECK_EQ(0, swscanf(x00, L"%w1i", (int *)NULL));
+ ATF_CHECK_EQ(0, swscanf(x00, L"%w128i", (int *)NULL));
+}
+
+ATF_TC_WITHOUT_HEAD(swscanf_wfN);
+ATF_TC_BODY(swscanf_wfN, tc)
+{
+ const wchar_t x00[] = L"0x00";
+ const wchar_t x7f[] = L"0x7fffffffffffffff";
+ const wchar_t xff[] = L"0xffffffffffffffff";
+
+#define SWSCANF_WFN_TEST(N, imin, umax) \
+ do { \
+ int_fast##N##_t i; \
+ uint_fast##N##_t u; \
+ ATF_CHECK_EQ(1, swscanf(x00, L"%wf" L(#N) L"i", &i)); \
+ ATF_CHECK_EQ(0, i); \
+ ATF_CHECK_EQ(1, swscanf(x7f, L"%wf" L(#N) L"i", &i)); \
+ ATF_CHECK_EQ(imin, i); \
+ ATF_CHECK_EQ(1, swscanf(x00, L"%wf" L(#N) L"x", &u)); \
+ ATF_CHECK_EQ(0, u); \
+ ATF_CHECK_EQ(1, swscanf(xff, L"%wf" L(#N) L"x", &u)); \
+ ATF_CHECK_EQ(umax, u); \
+ } while (0)
+ SWSCANF_WFN_TEST(8, -1, UINT_MAX);
+ SWSCANF_WFN_TEST(16, -1, UINT_MAX);
+ SWSCANF_WFN_TEST(32, -1, UINT_MAX);
+ SWSCANF_WFN_TEST(64, LLONG_MAX, ULLONG_MAX);
+#undef SWSCANF_WFN_TEST
+
+ ATF_CHECK_EQ(0, swscanf(x00, L"%wfi", (int *)NULL));
+ ATF_CHECK_EQ(0, swscanf(x00, L"%wf1i", (int *)NULL));
+ ATF_CHECK_EQ(0, swscanf(x00, L"%wf128i", (int *)NULL));
+}
+
+/*
+ * Test termination cases: non-numeric character, fixed width, EOF
+ */
+ATF_TC_WITHOUT_HEAD(swscanf_termination);
+ATF_TC_BODY(swscanf_termination, tc)
+{
+ int a = 0, b = 0, c = 0;
+ wchar_t d = 0;
+
+ ATF_CHECK_EQ(4, swscanf(L"3.1415", L"%d%lc%2d%d", &a, &d, &b, &c));
+ ATF_CHECK_EQ(3, a);
+ ATF_CHECK_EQ(14, b);
+ ATF_CHECK_EQ(15, c);
+ ATF_CHECK_EQ(L'.', d);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ setlocale(LC_NUMERIC, "en_US.UTF-8");
+ ATF_TP_ADD_TC(tp, swscanf_b);
+ ATF_TP_ADD_TC(tp, swscanf_o);
+ ATF_TP_ADD_TC(tp, swscanf_d);
+ ATF_TP_ADD_TC(tp, swscanf_x);
+ ATF_TP_ADD_TC(tp, swscanf_i);
+ ATF_TP_ADD_TC(tp, swscanf_wN);
+ ATF_TP_ADD_TC(tp, swscanf_wfN);
+ ATF_TP_ADD_TC(tp, swscanf_termination);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/Makefile b/lib/libc/tests/stdlib/Makefile
new file mode 100644
index 000000000000..9d84becfbd1f
--- /dev/null
+++ b/lib/libc/tests/stdlib/Makefile
@@ -0,0 +1,85 @@
+.include <src.opts.mk>
+
+ATF_TESTS_C+= clearenv_test
+ATF_TESTS_C+= cxa_atexit_test
+ATF_TESTS_C+= dynthr_test
+ATF_TESTS_C+= getenv_r_test
+ATF_TESTS_C+= heapsort_test
+ATF_TESTS_C+= libc_exit_test
+ATF_TESTS_C+= mergesort_test
+ATF_TESTS_C+= qsort_test
+.if ${COMPILER_FEATURES:Mblocks}
+ATF_TESTS_C+= qsort_b_test
+.endif
+ATF_TESTS_C+= qsort_r_compat_test
+ATF_TESTS_C+= qsort_r_test
+ATF_TESTS_C+= qsort_s_test
+ATF_TESTS_C+= qsort_bench
+ATF_TESTS_C+= set_constraint_handler_s_test
+ATF_TESTS_C+= strfmon_test
+ATF_TESTS_C+= tsearch_test
+ATF_TESTS_CXX+= cxa_thread_atexit_test
+ATF_TESTS_CXX+= cxa_thread_atexit_nothr_test
+
+# All architectures on FreeBSD have fenv.h
+CFLAGS+= -D__HAVE_FENV
+
+# Define __HAVE_LONG_DOUBLE for architectures whose long double has greater
+# precision than their double.
+.if ${MACHINE_CPUARCH} == "aarch64" || \
+ ${MACHINE_CPUARCH} == "amd64" || \
+ ${MACHINE_CPUARCH} == "i386" || \
+ ${MACHINE_CPUARCH} == "riscv"
+CFLAGS+= -D__HAVE_LONG_DOUBLE
+.endif
+
+# TODO: t_getenv_thread, t_mi_vector_hash, t_strtoi
+NETBSD_ATF_TESTS_C+= abs_test
+NETBSD_ATF_TESTS_C+= atoi_test
+NETBSD_ATF_TESTS_C+= div_test
+NETBSD_ATF_TESTS_C+= getenv_test
+NETBSD_ATF_TESTS_C+= exit_test
+NETBSD_ATF_TESTS_C+= hsearch_test
+NETBSD_ATF_TESTS_C+= posix_memalign_test
+NETBSD_ATF_TESTS_C+= random_test
+NETBSD_ATF_TESTS_C+= strtod_test
+NETBSD_ATF_TESTS_C+= strtol_test
+NETBSD_ATF_TESTS_C+= system_test
+
+# TODO: need to come up with a correct explanation of what the patch pho does
+# with h_atexit
+#ATF_TESTS_SH= atexit_test
+NETBSD_ATF_TESTS_SH= getopt_test
+
+.include "../Makefile.netbsd-tests"
+
+BINDIR= ${TESTSDIR}
+
+# TODO: see comment above
+#PROGS+= h_atexit
+PROGS+= h_getopt h_getopt_long
+
+CFLAGS+= -I${.CURDIR}
+
+LIBADD.cxa_thread_atexit_test+= pthread
+
+# Tests that require blocks support
+.for t in qsort_b_test
+CFLAGS.${t}.c+= -fblocks
+LIBADD.${t}+= BlocksRuntime
+.endfor
+
+.for t in h_getopt h_getopt_long
+CFLAGS.$t+= -I${LIBNETBSD_SRCDIR} -I${SRCTOP}/contrib/netbsd-tests
+LDFLAGS.$t+= -L${LIBNETBSD_OBJDIR}
+
+LIBADD.${t}+= netbsd util
+.endfor
+
+LIBADD.libc_exit_test+= pthread
+LIBADD.strtod_test+= m
+
+SUBDIR+= dynthr_mod
+SUBDIR+= libatexit
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/stdlib/Makefile.depend b/lib/libc/tests/stdlib/Makefile.depend
new file mode 100644
index 000000000000..777d716dbd01
--- /dev/null
+++ b/lib/libc/tests/stdlib/Makefile.depend
@@ -0,0 +1,24 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/atf/libatf-c++ \
+ lib/libc \
+ lib/libc++ \
+ lib/libcompiler_rt \
+ lib/libcxxrt \
+ lib/libnetbsd \
+ lib/libthr \
+ lib/libutil \
+ lib/msun \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/stdlib/clearenv_test.c b/lib/libc/tests/stdlib/clearenv_test.c
new file mode 100644
index 000000000000..003535a00060
--- /dev/null
+++ b/lib/libc/tests/stdlib/clearenv_test.c
@@ -0,0 +1,173 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2021 Mariusz Zaborski <oshogbo@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this 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.
+ */
+
+/*
+ * Test for clearenv(3) routine.
+ */
+
+#include <atf-c.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define TEST_VARIABLE "TEST_VAR"
+#define TEST_SYSTEM_VARIABLE "PWD"
+
+extern char **environ;
+
+void
+create_multiple_variables(int num)
+{
+ char name[64];
+ char value[64];
+ int i;
+
+ for (i = 0; i < num; i++) {
+ snprintf(name, sizeof(name), "%s_%d", TEST_VARIABLE, i);
+ snprintf(value, sizeof(value), "%d", i);
+ ATF_CHECK(getenv(name) == NULL);
+ ATF_CHECK(setenv(name, value, 0) != -1);
+ ATF_CHECK_STREQ(getenv(name), value);
+ }
+}
+
+void
+check_if_nulled_variables(int num)
+{
+ char name[64];
+ int i;
+
+ for (i = 0; i < num; i++) {
+ snprintf(name, sizeof(name), "%s_%d", TEST_VARIABLE, i);
+ ATF_CHECK(getenv(name) == NULL);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(clearenv__single_var_test);
+ATF_TC_BODY(clearenv__single_var_test, tc)
+{
+
+ ATF_CHECK(setenv(TEST_VARIABLE, "true", 0) != -1);
+ ATF_CHECK_STREQ(getenv(TEST_VARIABLE), "true");
+ ATF_CHECK(clearenv() == 0);
+ ATF_CHECK(getenv(TEST_VARIABLE) == NULL);
+}
+
+ATF_TC_WITHOUT_HEAD(clearenv__multiple_vars_test);
+ATF_TC_BODY(clearenv__multiple_vars_test, tc)
+{
+
+ create_multiple_variables(10);
+ ATF_CHECK(clearenv() == 0);
+ check_if_nulled_variables(10);
+}
+
+ATF_TC_WITHOUT_HEAD(clearenv__recreated_vars_test);
+ATF_TC_BODY(clearenv__recreated_vars_test, tc)
+{
+
+ create_multiple_variables(10);
+ ATF_CHECK(clearenv() == 0);
+ check_if_nulled_variables(10);
+ create_multiple_variables(10);
+}
+
+ATF_TC_WITHOUT_HEAD(clearenv__system_var_test);
+ATF_TC_BODY(clearenv__system_var_test, tc)
+{
+
+ ATF_CHECK(getenv(TEST_SYSTEM_VARIABLE) != NULL);
+ ATF_CHECK(clearenv() == 0);
+ ATF_CHECK(getenv(TEST_SYSTEM_VARIABLE) == NULL);
+}
+
+ATF_TC_WITHOUT_HEAD(clearenv__recreated_system_var_test);
+ATF_TC_BODY(clearenv__recreated_system_var_test, tc)
+{
+
+ ATF_CHECK(getenv(TEST_SYSTEM_VARIABLE) != NULL);
+ ATF_CHECK(clearenv() == 0);
+ ATF_CHECK(getenv(TEST_SYSTEM_VARIABLE) == NULL);
+ ATF_CHECK(setenv(TEST_SYSTEM_VARIABLE, "test", 0) != -1);
+ ATF_CHECK_STREQ(getenv(TEST_SYSTEM_VARIABLE), "test");
+}
+
+ATF_TC_WITHOUT_HEAD(clearenv__double_clear_vars);
+ATF_TC_BODY(clearenv__double_clear_vars, tc)
+{
+
+ create_multiple_variables(10);
+ ATF_CHECK(clearenv() == 0);
+ check_if_nulled_variables(10);
+ ATF_CHECK(clearenv() == 0);
+ check_if_nulled_variables(10);
+ create_multiple_variables(10);
+}
+
+ATF_TC_WITHOUT_HEAD(clearenv__environ_null);
+ATF_TC_BODY(clearenv__environ_null, tc)
+{
+
+ ATF_CHECK(clearenv() == 0);
+ ATF_CHECK(environ != NULL);
+}
+
+ATF_TC_WITHOUT_HEAD(clearenv__putenv_vars);
+ATF_TC_BODY(clearenv__putenv_vars, tc)
+{
+ char buf[64], ref[64];
+
+ snprintf(buf, sizeof(buf), "%s=1", TEST_VARIABLE);
+ strcpy(ref, buf);
+
+ ATF_CHECK(getenv(TEST_VARIABLE) == NULL);
+ ATF_CHECK(putenv(buf) != -1);
+ ATF_CHECK(strcmp(getenv(TEST_VARIABLE), "1") == 0);
+
+ ATF_CHECK(clearenv() == 0);
+
+ ATF_CHECK(getenv(TEST_VARIABLE) == NULL);
+ ATF_CHECK(strcmp(buf, ref) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, clearenv__single_var_test);
+ ATF_TP_ADD_TC(tp, clearenv__multiple_vars_test);
+ ATF_TP_ADD_TC(tp, clearenv__recreated_vars_test);
+
+ ATF_TP_ADD_TC(tp, clearenv__system_var_test);
+ ATF_TP_ADD_TC(tp, clearenv__recreated_system_var_test);
+
+ ATF_TP_ADD_TC(tp, clearenv__double_clear_vars);
+ ATF_TP_ADD_TC(tp, clearenv__environ_null);
+
+ ATF_TP_ADD_TC(tp, clearenv__putenv_vars);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/cxa_atexit_test.c b/lib/libc/tests/stdlib/cxa_atexit_test.c
new file mode 100644
index 000000000000..7e2cafbce850
--- /dev/null
+++ b/lib/libc/tests/stdlib/cxa_atexit_test.c
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (c) 2025 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/wait.h>
+
+#include <dlfcn.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+#define ARBITRARY_EXIT_CODE 42
+
+static char *
+get_shlib(const char *srcdir)
+{
+ char *shlib;
+
+ shlib = NULL;
+ if (asprintf(&shlib, "%s/libatexit.so", srcdir) < 0)
+ atf_tc_fail("failed to construct path to libatexit.so");
+ return (shlib);
+}
+
+static void
+run_test(const atf_tc_t *tc, bool with_fatal_atexit, bool with_exit)
+{
+ pid_t p;
+ void (*set_fatal_atexit)(bool);
+ void (*set_exit_code)(int);
+ void *hdl;
+ char *shlib;
+
+ shlib = get_shlib(atf_tc_get_config_var(tc, "srcdir"));
+
+ hdl = dlopen(shlib, RTLD_LAZY);
+ ATF_REQUIRE_MSG(hdl != NULL, "dlopen: %s", dlerror());
+
+ free(shlib);
+
+ if (with_fatal_atexit) {
+ set_fatal_atexit = dlsym(hdl, "set_fatal_atexit");
+ ATF_REQUIRE_MSG(set_fatal_atexit != NULL,
+ "set_fatal_atexit: %s", dlerror());
+ }
+ if (with_exit) {
+ set_exit_code = dlsym(hdl, "set_exit_code");
+ ATF_REQUIRE_MSG(set_exit_code != NULL, "set_exit_code: %s",
+ dlerror());
+ }
+
+ p = atf_utils_fork();
+ if (p == 0) {
+ /*
+ * Don't let the child clobber the results file; stderr/stdout
+ * have been replaced by atf_utils_fork() to capture it. We're
+ * intentionally using exit() instead of _exit() here to run
+ * __cxa_finalize at exit, otherwise we'd just leave it be.
+ */
+ closefrom(3);
+
+ if (with_fatal_atexit)
+ set_fatal_atexit(true);
+ if (with_exit)
+ set_exit_code(ARBITRARY_EXIT_CODE);
+
+ dlclose(hdl);
+
+ /*
+ * If the dtor was supposed to exit (most cases), then we should
+ * not have made it to this point. If it's not supposed to
+ * exit, then we just exit with success here because we might
+ * be expecting either a clean exit or a signal on our way out
+ * as the final __cxa_finalize tries to run a callback in the
+ * unloaded DSO.
+ */
+ if (with_exit)
+ exit(1);
+ exit(0);
+ }
+
+ dlclose(hdl);
+ atf_utils_wait(p, with_exit ? ARBITRARY_EXIT_CODE : 0, "", "");
+}
+
+ATF_TC_WITHOUT_HEAD(simple_cxa_atexit);
+ATF_TC_BODY(simple_cxa_atexit, tc)
+{
+ /*
+ * This test exits in a global object's dtor so that we check for our
+ * dtor being run at dlclose() time. If it isn't, then the forked child
+ * will have a chance to exit(1) after dlclose() to raise a failure.
+ */
+ run_test(tc, false, true);
+}
+
+ATF_TC_WITHOUT_HEAD(late_cxa_atexit);
+ATF_TC_BODY(late_cxa_atexit, tc)
+{
+ /*
+ * This test creates another global object during a __cxa_atexit handler
+ * invocation. It's been observed in the wild that we weren't executing
+ * it, then the DSO gets torn down and it was executed at application
+ * exit time instead. In the best case scenario we would crash if
+ * something else hadn't been mapped there.
+ */
+ run_test(tc, true, false);
+}
+
+ATF_TC_WITHOUT_HEAD(late_cxa_atexit_ran);
+ATF_TC_BODY(late_cxa_atexit_ran, tc)
+{
+ /*
+ * This is a slight variation of the previous test where we trigger an
+ * exit() in our late-registered __cxa_atexit handler so that we can
+ * ensure it was ran *before* dlclose() finished and not through some
+ * weird chain of events afterwards.
+ */
+ run_test(tc, true, true);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, simple_cxa_atexit);
+ ATF_TP_ADD_TC(tp, late_cxa_atexit);
+ ATF_TP_ADD_TC(tp, late_cxa_atexit_ran);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/cxa_thread_atexit_nothr_test.cc b/lib/libc/tests/stdlib/cxa_thread_atexit_nothr_test.cc
new file mode 100644
index 000000000000..0b3b9497a6bd
--- /dev/null
+++ b/lib/libc/tests/stdlib/cxa_thread_atexit_nothr_test.cc
@@ -0,0 +1,99 @@
+/*-
+ * Copyright (c) 2016 Mahdi Mokhtari <mokhi64@gmail.com>
+ * Copyright (c) 2016 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dlfcn.h>
+#include <atf-c++.hpp>
+#include <cstdio>
+#include <cstdlib>
+
+static FILE *output = NULL;
+
+struct Foo {
+ Foo() { ATF_REQUIRE(fprintf(output, "Created\n") > 0); }
+ ~Foo() { ATF_REQUIRE(fprintf(output, "Destroyed\n") > 0); }
+ void use() { ATF_REQUIRE(fprintf(output, "Used\n") > 0); }
+};
+
+static thread_local Foo f;
+
+/*
+ * This test must not be linked to libpthread.
+ */
+ATF_TEST_CASE_WITHOUT_HEAD(cxx__nothr);
+ATF_TEST_CASE_BODY(cxx__nothr)
+{
+ void *libthr_handle;
+
+ /* Avoid coredump during f construction. */
+ output = stderr;
+
+ libthr_handle = dlopen("libthr.so.3", RTLD_LAZY | RTLD_GLOBAL |
+ RTLD_NOLOAD);
+ ATF_REQUIRE(libthr_handle == NULL);
+}
+
+static void
+check_local_main(void)
+{
+ static const char out_log[] = "Created\nUsed\nDestroyed\n";
+
+ fflush(output);
+ ATF_REQUIRE(atf::utils::compare_file("test_main.txt", out_log));
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(cxx__thread_local_main);
+ATF_TEST_CASE_BODY(cxx__thread_local_main)
+{
+
+ ATF_REQUIRE((output = fopen("test_main.txt", "w")) != NULL);
+ f.use();
+ atexit(check_local_main);
+}
+
+extern "C" int __cxa_thread_atexit(void (*)(void *), void *, void *);
+
+static void
+again(void *arg)
+{
+
+ __cxa_thread_atexit(again, arg, &output);
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(cxx__thread_inf_dtors);
+ATF_TEST_CASE_BODY(cxx__thread_inf_dtors)
+{
+
+ again(NULL);
+}
+
+ATF_INIT_TEST_CASES(tcs)
+{
+
+ ATF_ADD_TEST_CASE(tcs, cxx__nothr);
+ ATF_ADD_TEST_CASE(tcs, cxx__thread_local_main);
+ ATF_ADD_TEST_CASE(tcs, cxx__thread_inf_dtors);
+}
diff --git a/lib/libc/tests/stdlib/cxa_thread_atexit_test.cc b/lib/libc/tests/stdlib/cxa_thread_atexit_test.cc
new file mode 100644
index 000000000000..628a70b510d1
--- /dev/null
+++ b/lib/libc/tests/stdlib/cxa_thread_atexit_test.cc
@@ -0,0 +1,177 @@
+/*-
+ * Copyright (c) 2016 Mahdi Mokhtari <mokhi64@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <dlfcn.h>
+#include <atf-c++.hpp>
+#include <cstdio>
+#include <cstdlib>
+#include <thread>
+
+static FILE *output = NULL;
+
+struct Foo {
+ Foo() { ATF_REQUIRE(fprintf(output, "Created\n") > 0); }
+ ~Foo() { ATF_REQUIRE(fprintf(output, "Destroyed\n") > 0); }
+ void use() { ATF_REQUIRE(fprintf(output, "Used\n") > 0); }
+};
+
+struct Bar {
+ Bar() {}
+ ~Bar() {
+ thread_local static Foo foo;
+ ATF_REQUIRE(fprintf(output, "DIED\n") > 0);
+ }
+ void use() {}
+};
+
+extern "C" int __cxa_thread_atexit(void (*)(void *), void *, void *);
+
+static void
+again(void *arg)
+{
+
+ __cxa_thread_atexit(again, arg, &output);
+}
+
+struct Baz {
+ Baz() {}
+ ~Baz() {
+ again(NULL);
+ }
+ void use() {}
+};
+
+static thread_local Foo f;
+static thread_local Foo g;
+static thread_local Bar h;
+static thread_local Baz e;
+
+/*
+ * This test must be linked to libpthread.
+ */
+ATF_TEST_CASE_WITHOUT_HEAD(cxx__thr);
+ATF_TEST_CASE_BODY(cxx__thr)
+{
+ void *libthr_handle;
+
+ /* Avoid coredump during f construction. */
+ output = stderr;
+
+ libthr_handle = dlopen("libthr.so.3", RTLD_LAZY | RTLD_GLOBAL |
+ RTLD_NOLOAD);
+ ATF_REQUIRE(libthr_handle != NULL);
+ dlclose(libthr_handle);
+}
+
+/*
+ * In this test f.use() will test cxa_thread_atexit() in non-threaded mode.
+ * After f.use() main will be threaded and we'll have one additional thread
+ * with its own TLS data.
+ */
+ATF_TEST_CASE_WITHOUT_HEAD(cxx__thread_local_before);
+ATF_TEST_CASE_BODY(cxx__thread_local_before)
+{
+ static const char out_log[] = "Created\nCreated\nUsed\nCreated\n"
+ "Created\nUsed\nCreated\nDIED\nDestroyed\nDestroyed\nDestroyed\n";
+
+ ATF_REQUIRE((output = fopen("test_before.txt", "w")) != NULL);
+
+ f.use();
+ std::thread t([]() { f.use(); });
+ t.join();
+
+ fflush(output);
+
+ ATF_REQUIRE(atf::utils::compare_file("test_before.txt", out_log));
+}
+
+/*
+ * In this test, f.use() will test __cxa_thread_atexit()
+ * in threaded mode (but still in main-threaed).
+ */
+ATF_TEST_CASE_WITHOUT_HEAD(cxx__thread_local_after);
+ATF_TEST_CASE_BODY(cxx__thread_local_after)
+{
+ static const char out_log[] = "Created\nCreated\nUsed\nCreated\n"
+ "DIED\nDestroyed\nDestroyed\nDestroyed\nCreated\nCreated\nUsed\n";
+
+ ATF_REQUIRE((output = fopen("test_after.txt", "w")) != NULL);
+
+ std::thread t([]() { g.use(); });
+ t.join();
+ sleep(1);
+ g.use();
+
+ fflush(output);
+
+ ATF_REQUIRE(atf::utils::compare_file("test_after.txt", out_log));
+}
+
+/*
+ * In this test, we register a new dtor while dtors are being run
+ * in __cxa_thread_atexit().
+ */
+ATF_TEST_CASE_WITHOUT_HEAD(cxx__thread_local_add_while_calling_dtors);
+ATF_TEST_CASE_BODY(cxx__thread_local_add_while_calling_dtors)
+{
+ static const char out_log[] = "Created\nCreated\nCreated\nDIED\n"
+ "Destroyed\nDestroyed\nDestroyed\n";
+
+ ATF_REQUIRE((output = fopen("test_add_meanwhile.txt", "w")) != NULL);
+
+ std::thread t([]() { h.use(); });
+ t.join();
+ sleep(1);
+
+ fflush(output);
+
+ ATF_REQUIRE(atf::utils::compare_file("test_add_meanwhile.txt", out_log));
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(cxx__thread_inf_dtors);
+ATF_TEST_CASE_BODY(cxx__thread_inf_dtors)
+{
+
+ /*
+ * Only added to make isolated run of this test not
+ * coredumping. Construction of Foo objects require filled
+ * output.
+ */
+ output = stderr;
+
+ std::thread t([]() { e.use(); });
+ t.join();
+}
+
+ATF_INIT_TEST_CASES(tcs)
+{
+
+ ATF_ADD_TEST_CASE(tcs, cxx__thr);
+ ATF_ADD_TEST_CASE(tcs, cxx__thread_local_before);
+ ATF_ADD_TEST_CASE(tcs, cxx__thread_local_after);
+ ATF_ADD_TEST_CASE(tcs, cxx__thread_local_add_while_calling_dtors);
+ ATF_ADD_TEST_CASE(tcs, cxx__thread_inf_dtors);
+}
diff --git a/lib/libc/tests/stdlib/dynthr_mod/Makefile b/lib/libc/tests/stdlib/dynthr_mod/Makefile
new file mode 100644
index 000000000000..3602e81e5be1
--- /dev/null
+++ b/lib/libc/tests/stdlib/dynthr_mod/Makefile
@@ -0,0 +1,10 @@
+SHLIB_NAME= dynthr_mod.so
+SHLIBDIR= ${TESTSDIR}
+SRCS= dynthr_mod.c
+LIBADD= pthread
+PACKAGE= tests
+
+TESTSDIR:= ${TESTSBASE}/${RELDIR:C/libc\/tests/libc/:H}
+
+
+.include <bsd.lib.mk>
diff --git a/lib/libc/tests/stdlib/dynthr_mod/dynthr_mod.c b/lib/libc/tests/stdlib/dynthr_mod/dynthr_mod.c
new file mode 100644
index 000000000000..eccffcd0bd49
--- /dev/null
+++ b/lib/libc/tests/stdlib/dynthr_mod/dynthr_mod.c
@@ -0,0 +1,71 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2019 Andrew Gierth
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this 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.
+ *
+ * Though this file is initially distributed under the 2-clause BSD license,
+ * the author grants permission for its redistribution under alternative
+ * licenses as set forth at <https://rhodiumtoad.github.io/RELICENSE.txt>.
+ * This paragraph and the RELICENSE.txt file are not part of the license and
+ * may be omitted in redistributions.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+
+void mod_main(int op);
+
+static pthread_t thr;
+
+static void *
+mod_thread(void *ptr __unused)
+{
+ char *volatile dummy;
+
+ dummy = malloc(500);
+ *dummy = 'a';
+ return (NULL);
+}
+
+void
+mod_main(int op)
+{
+ int rc;
+
+ switch (op) {
+ case 1:
+ rc = pthread_create(&thr, NULL, mod_thread, NULL);
+ if (rc != 0)
+ _exit(1);
+ break;
+ case 0:
+ pthread_join(thr, NULL);
+ break;
+ }
+}
+
diff --git a/lib/libc/tests/stdlib/dynthr_test.c b/lib/libc/tests/stdlib/dynthr_test.c
new file mode 100644
index 000000000000..5b0af718afc7
--- /dev/null
+++ b/lib/libc/tests/stdlib/dynthr_test.c
@@ -0,0 +1,90 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2019 Andrew Gierth
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this 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.
+ *
+ * Though this file is initially distributed under the 2-clause BSD license,
+ * the author grants permission for its redistribution under alternative
+ * licenses as set forth at <https://rhodiumtoad.github.io/RELICENSE.txt>.
+ * This paragraph and the RELICENSE.txt file are not part of the license and
+ * may be omitted in redistributions.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <dlfcn.h>
+
+#include <atf-c.h>
+
+typedef void (modfunc_t)(int op);
+
+/*
+ * Minimal test case for PR 235158; mutual dependencies between jemalloc and
+ * libthr causing issues in thread creation. Specifically to this case, libthr
+ * uses calloc to initialize pthread mutexes, and jemalloc uses pthread mutexes.
+ *
+ * Deferred initialization provided by jemalloc proved to be fragile, causing
+ * issues like in the referenced PR where thread creation in a shared object
+ * loaded via dlopen(3) would stall unless the calling application also linked
+ * against pthread.
+ */
+ATF_TC(maintc);
+ATF_TC_HEAD(maintc, tc)
+{
+
+ atf_tc_set_md_var(tc, "timeout", "3");
+}
+
+ATF_TC_BODY(maintc, tc)
+{
+ char *libpath;
+ modfunc_t *func;
+ void *mod_handle;
+ const char *srcdir;
+ dlfunc_t rawfunc;
+
+ srcdir = atf_tc_get_config_var(tc, "srcdir");
+ if (asprintf(&libpath, "%s/dynthr_mod.so", srcdir) < 0)
+ atf_tc_fail("failed to construct path to libthr");
+ mod_handle = dlopen(libpath, RTLD_LOCAL);
+ free(libpath);
+ if (mod_handle == NULL)
+ atf_tc_fail("failed to open dynthr_mod.so: %s", dlerror());
+ rawfunc = dlfunc(mod_handle, "mod_main");
+ if (rawfunc == NULL)
+ atf_tc_fail("failed to resolve function mod_main");
+ func = (modfunc_t *)rawfunc;
+ func(1);
+ func(0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, maintc);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/getenv_r_test.c b/lib/libc/tests/stdlib/getenv_r_test.c
new file mode 100644
index 000000000000..8085b92b1064
--- /dev/null
+++ b/lib/libc/tests/stdlib/getenv_r_test.c
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(getenv_r_ok);
+ATF_TC_BODY(getenv_r_ok, tc)
+{
+ const char *ident = atf_tc_get_ident(tc);
+ char buf[256];
+
+ ATF_REQUIRE_EQ(0, setenv("ATF_TC_IDENT", ident, 1));
+ ATF_REQUIRE_EQ(0, getenv_r("ATF_TC_IDENT", buf, sizeof(buf)));
+ ATF_REQUIRE_STREQ(ident, buf);
+}
+
+ATF_TC_WITHOUT_HEAD(getenv_r_einval);
+ATF_TC_BODY(getenv_r_einval, tc)
+{
+ char buf[256];
+
+ errno = 0;
+ ATF_REQUIRE_EQ(-1, getenv_r(NULL, buf, sizeof(buf)));
+ ATF_REQUIRE_EQ(EINVAL, errno);
+ errno = 0;
+ ATF_REQUIRE_EQ(-1, getenv_r("", buf, sizeof(buf)));
+ ATF_REQUIRE_EQ(EINVAL, errno);
+ errno = 0;
+ ATF_REQUIRE_EQ(-1, getenv_r("A=B", buf, sizeof(buf)));
+ ATF_REQUIRE_EQ(EINVAL, errno);
+}
+
+ATF_TC_WITHOUT_HEAD(getenv_r_enoent);
+ATF_TC_BODY(getenv_r_enoent, tc)
+{
+ char buf[256];
+
+ errno = 0;
+ ATF_REQUIRE_EQ(-1, getenv_r("no such variable", buf, sizeof(buf)));
+ ATF_REQUIRE_EQ(ENOENT, errno);
+}
+
+ATF_TC_WITHOUT_HEAD(getenv_r_erange);
+ATF_TC_BODY(getenv_r_erange, tc)
+{
+ const char *ident = atf_tc_get_ident(tc);
+ char buf[256];
+
+ ATF_REQUIRE_EQ(0, setenv("ATF_TC_IDENT", ident, 1));
+ errno = 0;
+ ATF_REQUIRE_EQ(-1, getenv_r("ATF_TC_IDENT", buf, strlen(ident)));
+ ATF_REQUIRE_EQ(ERANGE, errno);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, getenv_r_ok);
+ ATF_TP_ADD_TC(tp, getenv_r_einval);
+ ATF_TP_ADD_TC(tp, getenv_r_enoent);
+ ATF_TP_ADD_TC(tp, getenv_r_erange);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/heapsort_test.c b/lib/libc/tests/stdlib/heapsort_test.c
new file mode 100644
index 000000000000..b6747ff5d3aa
--- /dev/null
+++ b/lib/libc/tests/stdlib/heapsort_test.c
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (C) 2004 Maxim Sobolev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test for heapsort() routine.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "test-sort.h"
+
+ATF_TC_WITHOUT_HEAD(heapsort_test);
+ATF_TC_BODY(heapsort_test, tc)
+{
+ int sresvector[IVEC_LEN];
+ int testvector[IVEC_LEN];
+ int i, j;
+
+ for (j = 2; j < IVEC_LEN; j++) {
+ /* Populate test vectors */
+ for (i = 0; i < j; i++)
+ testvector[i] = sresvector[i] = initvector[i];
+
+ /* Sort using heapsort(3) */
+ heapsort(testvector, j, sizeof(testvector[0]), sorthelp);
+ /* Sort using reference slow sorting routine */
+ ssort(sresvector, j);
+
+ /* Compare results */
+ for (i = 0; i < j; i++)
+ ATF_CHECK_MSG(testvector[i] == sresvector[i],
+ "item at index %d didn't match: %d != %d",
+ i, testvector[i], sresvector[i]);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, heapsort_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/libatexit/Makefile b/lib/libc/tests/stdlib/libatexit/Makefile
new file mode 100644
index 000000000000..9ba04c77af62
--- /dev/null
+++ b/lib/libc/tests/stdlib/libatexit/Makefile
@@ -0,0 +1,11 @@
+SHLIB_CXX= libatexit
+SHLIB_NAME= libatexit.so
+SHLIB_MAJOR= 1
+SHLIBDIR= ${TESTSDIR}
+PACKAGE= tests
+SRCS= libatexit.cc
+
+TESTSDIR:= ${TESTSBASE}/${RELDIR:C/libc\/tests/libc/:H}
+
+
+.include <bsd.lib.mk>
diff --git a/lib/libc/tests/stdlib/libatexit/libatexit.cc b/lib/libc/tests/stdlib/libatexit/libatexit.cc
new file mode 100644
index 000000000000..bb286c97e421
--- /dev/null
+++ b/lib/libc/tests/stdlib/libatexit/libatexit.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2025 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ */
+
+#include <unistd.h>
+
+static int exit_code = -1;
+static bool fatal_atexit;
+
+extern "C" {
+ void set_fatal_atexit(bool);
+ void set_exit_code(int);
+}
+
+void
+set_fatal_atexit(bool fexit)
+{
+ fatal_atexit = fexit;
+}
+
+void
+set_exit_code(int code)
+{
+ exit_code = code;
+}
+
+struct other_object {
+ ~other_object() {
+
+ /*
+ * In previous versions of our __cxa_atexit handling, we would
+ * never actually execute this handler because it's added during
+ * ~object() below; __cxa_finalize would never revisit it. We
+ * will allow the caller to configure us to exit with a certain
+ * exit code so that it can run us twice: once to ensure we
+ * don't crash at the end, and again to make sure the handler
+ * actually ran.
+ */
+ if (exit_code != -1)
+ _exit(exit_code);
+ }
+};
+
+void
+create_staticobj()
+{
+ static other_object obj;
+}
+
+struct object {
+ ~object() {
+ /*
+ * If we're doing the fatal_atexit behavior (i.e., create an
+ * object that will add its own dtor for __cxa_finalize), then
+ * we don't exit here.
+ */
+ if (fatal_atexit)
+ create_staticobj();
+ else if (exit_code != -1)
+ _exit(exit_code);
+ }
+};
+
+static object obj;
diff --git a/lib/libc/tests/stdlib/libc_exit_test.c b/lib/libc/tests/stdlib/libc_exit_test.c
new file mode 100644
index 000000000000..12965261bdb3
--- /dev/null
+++ b/lib/libc/tests/stdlib/libc_exit_test.c
@@ -0,0 +1,154 @@
+/*-
+ * Copyright (c) 2023 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/wait.h>
+
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+static void
+func_a(void)
+{
+ if (write(STDOUT_FILENO, "a", 1) != 1)
+ _Exit(1);
+}
+
+static void
+func_b(void)
+{
+ if (write(STDOUT_FILENO, "b", 1) != 1)
+ _Exit(1);
+}
+
+static void
+func_c(void)
+{
+ if (write(STDOUT_FILENO, "c", 1) != 1)
+ _Exit(1);
+}
+
+static void
+child(void)
+{
+ /* this will be received by the parent */
+ printf("hello, ");
+ fflush(stdout);
+ /* this won't, because quick_exit() does not flush */
+ printf("world");
+ /* these will be called in reverse order, producing "abc" */
+ if (at_quick_exit(func_c) != 0 ||
+ at_quick_exit(func_b) != 0 ||
+ at_quick_exit(func_a) != 0)
+ _Exit(1);
+ quick_exit(0);
+}
+
+ATF_TC_WITHOUT_HEAD(quick_exit);
+ATF_TC_BODY(quick_exit, tc)
+{
+ char buf[100] = "";
+ ssize_t len;
+ int p[2], wstatus = 0;
+ pid_t pid;
+
+ ATF_REQUIRE(pipe(p) == 0);
+ pid = fork();
+ if (pid == 0) {
+ if (dup2(p[1], STDOUT_FILENO) < 0)
+ _Exit(1);
+ (void)close(p[1]);
+ (void)close(p[0]);
+ child();
+ _Exit(1);
+ }
+ ATF_REQUIRE_MSG(pid > 0,
+ "expect fork() to succeed");
+ ATF_CHECK_EQ_MSG(pid, waitpid(pid, &wstatus, 0),
+ "expect to collect child process");
+ ATF_CHECK_EQ_MSG(0, wstatus,
+ "expect child to exit cleanly");
+ ATF_CHECK_MSG((len = read(p[0], buf, sizeof(buf))) > 0,
+ "expect to receive output from child");
+ ATF_CHECK_STREQ("hello, abc", buf);
+}
+
+static void
+myatexit1(void)
+{
+ exit(12);
+}
+
+ATF_TC_WITHOUT_HEAD(recursive_exit1);
+ATF_TC_BODY(recursive_exit1, tc)
+{
+ pid_t pid;
+ int wstatus;
+
+ pid = fork();
+ if (pid == 0) {
+ atexit(myatexit1);
+ exit(1);
+ }
+ ATF_REQUIRE_MSG(pid > 0,
+ "expect fork() to succeed");
+ ATF_CHECK_EQ_MSG(pid, waitpid(pid, &wstatus, 0),
+ "expect to collect child process");
+ ATF_CHECK(WIFEXITED(wstatus));
+ ATF_CHECK_EQ(WEXITSTATUS(wstatus), 12);
+}
+
+static pthread_barrier_t barrier;
+
+static void
+myatexit2(void)
+{
+ pthread_barrier_wait(&barrier);
+ exit(12);
+}
+
+static void *
+mythreadexit(void *arg)
+{
+ pthread_barrier_wait(&barrier);
+ exit(15);
+}
+
+ATF_TC_WITHOUT_HEAD(recursive_exit2);
+ATF_TC_BODY(recursive_exit2, tc)
+{
+ pid_t pid;
+ int wstatus;
+
+ pid = fork();
+ if (pid == 0) {
+ pthread_t thr;
+
+ atexit(myatexit2);
+
+ pthread_barrier_init(&barrier, NULL, 2);
+ pthread_create(&thr, NULL, mythreadexit, NULL);
+
+ exit(1);
+ }
+ ATF_REQUIRE_MSG(pid > 0,
+ "expect fork() to succeed");
+ ATF_CHECK_EQ_MSG(pid, waitpid(pid, &wstatus, 0),
+ "expect to collect child process");
+ ATF_CHECK(WIFEXITED(wstatus));
+ ATF_CHECK_EQ(WEXITSTATUS(wstatus), 12);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, quick_exit);
+ ATF_TP_ADD_TC(tp, recursive_exit1);
+ ATF_TP_ADD_TC(tp, recursive_exit2);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/mergesort_test.c b/lib/libc/tests/stdlib/mergesort_test.c
new file mode 100644
index 000000000000..156d6bb7c2c3
--- /dev/null
+++ b/lib/libc/tests/stdlib/mergesort_test.c
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (C) 2004 Maxim Sobolev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test for mergesort() routine.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "test-sort.h"
+
+ATF_TC_WITHOUT_HEAD(mergesort_test);
+ATF_TC_BODY(mergesort_test, tc)
+{
+ int sresvector[IVEC_LEN];
+ int testvector[IVEC_LEN];
+ int i, j;
+
+ for (j = 2; j < IVEC_LEN; j++) {
+ /* Populate test vectors */
+ for (i = 0; i < j; i++)
+ testvector[i] = sresvector[i] = initvector[i];
+
+ /* Sort using mergesort(3) */
+ mergesort(testvector, j, sizeof(testvector[0]), sorthelp);
+ /* Sort using reference slow sorting routine */
+ ssort(sresvector, j);
+
+ /* Compare results */
+ for (i = 0; i < j; i++)
+ ATF_CHECK_MSG(testvector[i] == sresvector[i],
+ "item at index %d didn't match: %d != %d",
+ i, testvector[i], sresvector[i]);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mergesort_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/qsort_b_test.c b/lib/libc/tests/stdlib/qsort_b_test.c
new file mode 100644
index 000000000000..60cd30ac222a
--- /dev/null
+++ b/lib/libc/tests/stdlib/qsort_b_test.c
@@ -0,0 +1,76 @@
+/*-
+ * Copyright (C) 2004 Maxim Sobolev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test for qsort_b() routine.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "test-sort.h"
+
+ATF_TC_WITHOUT_HEAD(qsort_b_test);
+ATF_TC_BODY(qsort_b_test, tc)
+{
+ int testvector[IVEC_LEN];
+ int sresvector[IVEC_LEN];
+ int i, j;
+
+ for (j = 2; j < IVEC_LEN; j++) {
+ /* Populate test vectors */
+ for (i = 0; i < j; i++)
+ testvector[i] = sresvector[i] = initvector[i];
+
+ /* Sort using qsort_b(3) */
+ qsort_b(testvector, j, sizeof(testvector[0]),
+ ^(const void* a, const void* b) {
+ if (*(int *)a > *(int *)b)
+ return (1);
+ else if (*(int *)a < *(int *)b)
+ return (-1);
+ else
+ return (0);
+ });
+ /* Sort using reference slow sorting routine */
+ ssort(sresvector, j);
+
+ /* Compare results */
+ for (i = 0; i < j; i++)
+ ATF_CHECK_MSG(testvector[i] == sresvector[i],
+ "item at index %d didn't match: %d != %d",
+ i, testvector[i], sresvector[i]);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, qsort_b_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/qsort_bench.c b/lib/libc/tests/stdlib/qsort_bench.c
new file mode 100644
index 000000000000..5f2cfae40140
--- /dev/null
+++ b/lib/libc/tests/stdlib/qsort_bench.c
@@ -0,0 +1,113 @@
+/*-
+ * Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <atf-c.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+/*-
+ * Measures qsort(3) runtime with pathological input and verify that it
+ * stays close to N * log2(N).
+ *
+ * Thanks to Vivian Hussey for the proof of concept.
+ *
+ * The input we construct is similar to a sweep from 0 to N where each
+ * half, except for the first element, has been reversed; for instance,
+ * with N = 8, we get { 0, 3, 2, 1, 4, 8, 7, 6 }. This triggers a bug in
+ * the BSD qsort(3) where it will switch to insertion sort if the pivots
+ * are sorted.
+ *
+ * This article goes into more detail about the bug and its origin:
+ *
+ * https://www.raygard.net/2022/02/26/Re-engineering-a-qsort-part-3
+ *
+ * With this optimization (the `if (swap_cnt == 0)` block), qsort(3) needs
+ * roughly N * N / 4 comparisons to sort our pathological input. Without
+ * it, it needs only a little more than N * log2(N) comparisons.
+ */
+
+/* we stop testing once a single takes longer than this */
+#define MAXRUNSECS 10
+
+static bool debugging;
+
+static uintmax_t ncmp;
+
+static int
+intcmp(const void *a, const void *b)
+{
+ ncmp++;
+ return ((*(int *)a > *(int *)b) - (*(int *)a < *(int *)b));
+}
+
+static void
+qsort_bench(int log2n)
+{
+ uintmax_t n = 1LLU << log2n;
+ int *buf;
+
+ /* fill an array with a pathological pattern */
+ ATF_REQUIRE(buf = malloc(n * sizeof(*buf)));
+ buf[0] = 0;
+ buf[n / 2] = n / 2;
+ for (unsigned int i = 1; i < n / 2; i++) {
+ buf[i] = n / 2 - i;
+ buf[n / 2 + i] = n - i;
+ }
+
+ ncmp = 0;
+ qsort(buf, n, sizeof(*buf), intcmp);
+
+ /* check result and free array */
+ if (debugging) {
+ for (unsigned int i = 1; i < n; i++) {
+ ATF_REQUIRE_MSG(buf[i] > buf[i - 1],
+ "array is not sorted");
+ }
+ }
+ free(buf);
+
+ /* check that runtime does not exceed N² */
+ ATF_CHECK_MSG(ncmp / n < n,
+ "runtime %ju exceeds N² for N = %ju", ncmp, n);
+
+ /* check that runtime does not exceed N log N by much */
+ ATF_CHECK_MSG(ncmp / n <= log2n + 1,
+ "runtime %ju exceeds N log N for N = %ju", ncmp, n);
+}
+
+ATF_TC_WITHOUT_HEAD(qsort_bench);
+ATF_TC_BODY(qsort_bench, tc)
+{
+ struct timespec t0, t1;
+ uintmax_t tus;
+
+ for (int i = 10; i <= 30; i++) {
+ clock_gettime(CLOCK_UPTIME, &t0);
+ qsort_bench(i);
+ clock_gettime(CLOCK_UPTIME, &t1);
+ tus = t1.tv_sec * 1000000 + t1.tv_nsec / 1000;
+ tus -= t0.tv_sec * 1000000 + t0.tv_nsec / 1000;
+ if (debugging) {
+ fprintf(stderr, "N = 2^%d in %ju.%06jus\n",
+ i, tus / 1000000, tus % 1000000);
+ }
+ /* stop once an individual run exceeds our limit */
+ if (tus / 1000000 >= MAXRUNSECS)
+ break;
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ debugging = !getenv("__RUNNING_INSIDE_ATF_RUN") &&
+ isatty(STDERR_FILENO);
+ ATF_TP_ADD_TC(tp, qsort_bench);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/qsort_r_compat_test.c b/lib/libc/tests/stdlib/qsort_r_compat_test.c
new file mode 100644
index 000000000000..d7b06615292e
--- /dev/null
+++ b/lib/libc/tests/stdlib/qsort_r_compat_test.c
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (C) 2020 Edward Tomasz Napierala <trasz@FreeBSD.org>
+ * Copyright (C) 2004 Maxim Sobolev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test for historical qsort_r(3) routine.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "test-sort.h"
+
+#define THUNK 42
+
+static int
+sorthelp_r(void *thunk, const void *a, const void *b)
+{
+ const int *oa, *ob;
+
+ ATF_REQUIRE_EQ(*(int *)thunk, THUNK);
+
+ oa = a;
+ ob = b;
+ /* Don't use "return *oa - *ob" since it's easy to cause overflow! */
+ if (*oa > *ob)
+ return (1);
+ if (*oa < *ob)
+ return (-1);
+ return (0);
+}
+
+ATF_TC_WITHOUT_HEAD(qsort_r_compat_test);
+ATF_TC_BODY(qsort_r_compat_test, tc)
+{
+ int testvector[IVEC_LEN];
+ int sresvector[IVEC_LEN];
+ int i, j;
+ int thunk = THUNK;
+
+ for (j = 2; j < IVEC_LEN; j++) {
+ /* Populate test vectors */
+ for (i = 0; i < j; i++)
+ testvector[i] = sresvector[i] = initvector[i];
+
+ /* Sort using qsort_r(3) */
+ qsort_r(testvector, j, sizeof(testvector[0]), &thunk,
+ sorthelp_r);
+ /* Sort using reference slow sorting routine */
+ ssort(sresvector, j);
+
+ /* Compare results */
+ for (i = 0; i < j; i++)
+ ATF_CHECK_MSG(testvector[i] == sresvector[i],
+ "item at index %d didn't match: %d != %d",
+ i, testvector[i], sresvector[i]);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, qsort_r_compat_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/qsort_r_test.c b/lib/libc/tests/stdlib/qsort_r_test.c
new file mode 100644
index 000000000000..446d63279fbf
--- /dev/null
+++ b/lib/libc/tests/stdlib/qsort_r_test.c
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (C) 2020 Edward Tomasz Napierala <trasz@FreeBSD.org>
+ * Copyright (C) 2004 Maxim Sobolev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test for qsort_r(3) routine.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "test-sort.h"
+
+#define THUNK 42
+
+static int
+sorthelp_r(const void *a, const void *b, void *thunk)
+{
+ const int *oa, *ob;
+
+ ATF_REQUIRE_EQ(*(int *)thunk, THUNK);
+
+ oa = a;
+ ob = b;
+ /* Don't use "return *oa - *ob" since it's easy to cause overflow! */
+ if (*oa > *ob)
+ return (1);
+ if (*oa < *ob)
+ return (-1);
+ return (0);
+}
+
+ATF_TC_WITHOUT_HEAD(qsort_r_test);
+ATF_TC_BODY(qsort_r_test, tc)
+{
+ int testvector[IVEC_LEN];
+ int sresvector[IVEC_LEN];
+ int i, j;
+ int thunk = THUNK;
+
+ for (j = 2; j < IVEC_LEN; j++) {
+ /* Populate test vectors */
+ for (i = 0; i < j; i++)
+ testvector[i] = sresvector[i] = initvector[i];
+
+ /* Sort using qsort_r(3) */
+ qsort_r(testvector, j, sizeof(testvector[0]), sorthelp_r,
+ &thunk);
+ /* Sort using reference slow sorting routine */
+ ssort(sresvector, j);
+
+ /* Compare results */
+ for (i = 0; i < j; i++)
+ ATF_CHECK_MSG(testvector[i] == sresvector[i],
+ "item at index %d didn't match: %d != %d",
+ i, testvector[i], sresvector[i]);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, qsort_r_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/qsort_s_test.c b/lib/libc/tests/stdlib/qsort_s_test.c
new file mode 100644
index 000000000000..c3210caf24e1
--- /dev/null
+++ b/lib/libc/tests/stdlib/qsort_s_test.c
@@ -0,0 +1,254 @@
+/*-
+ * Copyright (C) 2020 Edward Tomasz Napierala <trasz@FreeBSD.org>
+ * Copyright (c) 2017 Juniper Networks. All rights reserved.
+ * Copyright (C) 2004 Maxim Sobolev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test for qsort_s(3) routine.
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define THUNK 42
+
+#include "test-sort.h"
+
+static errno_t e;
+
+static int
+sorthelp_s(const void *a, const void *b, void *thunk)
+{
+ const int *oa, *ob;
+
+ ATF_REQUIRE_EQ(*(int *)thunk, THUNK);
+
+ oa = a;
+ ob = b;
+ /* Don't use "return *oa - *ob" since it's easy to cause overflow! */
+ if (*oa > *ob)
+ return (1);
+ if (*oa < *ob)
+ return (-1);
+ return (0);
+}
+
+void
+h(const char * restrict msg __unused, void * restrict ptr __unused, errno_t error)
+{
+ e = error;
+}
+
+/* nmemb < 0 */
+ATF_TC_WITHOUT_HEAD(qsort_s_nmemb_lt_zero);
+ATF_TC_BODY(qsort_s_nmemb_lt_zero, tc)
+{
+ int thunk = THUNK;
+ int b;
+
+ ATF_CHECK(qsort_s(&b, -1, sizeof(int), sorthelp_s, &thunk) != 0);
+}
+
+/* nmemb > rmax */
+ATF_TC_WITHOUT_HEAD(qsort_s_nmemb_gt_rmax);
+ATF_TC_BODY(qsort_s_nmemb_gt_rmax, tc)
+{
+ int thunk = THUNK;
+ int b;
+
+ ATF_CHECK(qsort_s(&b, RSIZE_MAX + 1, sizeof(int), sorthelp_s, &thunk) != 0);
+}
+
+/* size < 0 */
+ATF_TC_WITHOUT_HEAD(qsort_s_size_lt_zero);
+ATF_TC_BODY(qsort_s_size_lt_zero, tc)
+{
+ int thunk = THUNK;
+ int b;
+
+ ATF_CHECK(qsort_s(&b, 1, -1, sorthelp_s, &thunk) != 0);
+}
+
+/* size > rmax */
+ATF_TC_WITHOUT_HEAD(qsort_s_size_gt_rmax);
+ATF_TC_BODY(qsort_s_size_gt_rmax, tc)
+{
+ int thunk = THUNK;
+ int b;
+
+ ATF_CHECK(qsort_s(&b, 1, RSIZE_MAX + 1, sorthelp_s, &thunk) != 0);
+}
+
+/* NULL compar */
+ATF_TC_WITHOUT_HEAD(qsort_s_null_compar);
+ATF_TC_BODY(qsort_s_null_compar, tc)
+{
+ int thunk = THUNK;
+ int b;
+
+ ATF_CHECK(qsort_s(&b, 1, sizeof(int), NULL, &thunk) != 0);
+}
+
+/* nmemb < 0, handler */
+ATF_TC_WITHOUT_HEAD(qsort_s_nmemb_lt_zero_h);
+ATF_TC_BODY(qsort_s_nmemb_lt_zero_h, tc)
+{
+ int thunk = THUNK;
+ int b[] = {81, 4, 7};
+
+ e = 0;
+ set_constraint_handler_s(h);
+ ATF_CHECK(qsort_s(&b, -1, sizeof(int), sorthelp_s, &thunk) != 0);
+ ATF_CHECK(e > 0);
+ ATF_CHECK_EQ(b[0], 81);
+ ATF_CHECK_EQ(b[1], 4);
+ ATF_CHECK_EQ(b[2], 7);
+}
+
+/* nmemb > rmax, handler */
+ATF_TC_WITHOUT_HEAD(qsort_s_nmemb_gt_rmax_h);
+ATF_TC_BODY(qsort_s_nmemb_gt_rmax_h, tc)
+{
+ int thunk = THUNK;
+ int b[] = {81, 4, 7};
+
+ e = 0;
+ set_constraint_handler_s(h);
+ ATF_CHECK(qsort_s(&b, RSIZE_MAX + 1, sizeof(int), sorthelp_s, &thunk) != 0);
+ ATF_CHECK(e > 0);
+ ATF_CHECK_EQ(b[0], 81);
+ ATF_CHECK_EQ(b[1], 4);
+ ATF_CHECK_EQ(b[2], 7);
+}
+
+/* size < 0, handler */
+ATF_TC_WITHOUT_HEAD(qsort_s_size_lt_zero_h);
+ATF_TC_BODY(qsort_s_size_lt_zero_h, tc)
+{
+ int thunk = THUNK;
+ int b[] = {81, 4, 7};
+
+ e = 0;
+ set_constraint_handler_s(h);
+ ATF_CHECK(qsort_s(&b, nitems(b), -1, sorthelp_s, &thunk) != 0);
+ ATF_CHECK(e > 0);
+ ATF_CHECK_EQ(b[0], 81);
+ ATF_CHECK_EQ(b[1], 4);
+ ATF_CHECK_EQ(b[2], 7);
+}
+
+/* size > rmax, handler */
+ATF_TC_WITHOUT_HEAD(qsort_s_size_gt_rmax_h);
+ATF_TC_BODY(qsort_s_size_gt_rmax_h, tc)
+{
+ int thunk = THUNK;
+ int b[] = {81, 4, 7};
+
+ e = 0;
+ set_constraint_handler_s(h);
+ ATF_CHECK(qsort_s(&b, nitems(b), RSIZE_MAX + 1, sorthelp_s, &thunk) != 0);
+ ATF_CHECK(e > 0);
+ ATF_CHECK_EQ(b[0], 81);
+ ATF_CHECK_EQ(b[1], 4);
+ ATF_CHECK_EQ(b[2], 7);
+}
+
+/* NULL compar, handler */
+ATF_TC_WITHOUT_HEAD(qsort_s_null_compar_h);
+ATF_TC_BODY(qsort_s_null_compar_h, tc)
+{
+ int thunk = THUNK;
+ int b[] = {81, 4, 7};
+
+ e = 0;
+ set_constraint_handler_s(h);
+ ATF_CHECK(qsort_s(&b, nitems(b), sizeof(int), NULL, &thunk) != 0);
+ ATF_CHECK(e > 0);
+ ATF_CHECK_EQ(b[0], 81);
+ ATF_CHECK_EQ(b[1], 4);
+ ATF_CHECK_EQ(b[2], 7);
+}
+
+ATF_TC_WITHOUT_HEAD(qsort_s_h);
+ATF_TC_BODY(qsort_s_h, tc)
+{
+ int thunk = THUNK;
+ int b[] = {81, 4, 7};
+
+ e = 0;
+ set_constraint_handler_s(h);
+ ATF_CHECK(qsort_s(&b, nitems(b), sizeof(int), sorthelp_s, &thunk) == 0);
+ ATF_CHECK(e == 0);
+ ATF_CHECK_EQ(b[0], 4);
+ ATF_CHECK_EQ(b[1], 7);
+ ATF_CHECK_EQ(b[2], 81);
+}
+
+ATF_TC_WITHOUT_HEAD(qsort_s_test);
+ATF_TC_BODY(qsort_s_test, tc)
+{
+ int testvector[IVEC_LEN];
+ int sresvector[IVEC_LEN];
+ int i, j;
+ int thunk = THUNK;
+
+ for (j = 2; j < IVEC_LEN; j++) {
+ /* Populate test vectors */
+ for (i = 0; i < j; i++)
+ testvector[i] = sresvector[i] = initvector[i];
+
+ /* Sort using qsort_s(3) */
+ qsort_s(testvector, j, sizeof(testvector[0]),
+ sorthelp_s, &thunk);
+ /* Sort using reference slow sorting routine */
+ ssort(sresvector, j);
+
+ /* Compare results */
+ for (i = 0; i < j; i++)
+ ATF_CHECK_MSG(testvector[i] == sresvector[i],
+ "item at index %d didn't match: %d != %d",
+ i, testvector[i], sresvector[i]);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, qsort_s_nmemb_lt_zero);
+ ATF_TP_ADD_TC(tp, qsort_s_nmemb_gt_rmax);
+ ATF_TP_ADD_TC(tp, qsort_s_size_lt_zero);
+ ATF_TP_ADD_TC(tp, qsort_s_size_gt_rmax);
+ ATF_TP_ADD_TC(tp, qsort_s_null_compar);
+ ATF_TP_ADD_TC(tp, qsort_s_nmemb_lt_zero_h);
+ ATF_TP_ADD_TC(tp, qsort_s_nmemb_gt_rmax_h);
+ ATF_TP_ADD_TC(tp, qsort_s_size_lt_zero_h);
+ ATF_TP_ADD_TC(tp, qsort_s_size_gt_rmax_h);
+ ATF_TP_ADD_TC(tp, qsort_s_null_compar_h);
+ ATF_TP_ADD_TC(tp, qsort_s_h);
+ ATF_TP_ADD_TC(tp, qsort_s_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/qsort_test.c b/lib/libc/tests/stdlib/qsort_test.c
new file mode 100644
index 000000000000..a6202e55fd62
--- /dev/null
+++ b/lib/libc/tests/stdlib/qsort_test.c
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (C) 2004 Maxim Sobolev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Test for qsort() routine.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "test-sort.h"
+
+ATF_TC_WITHOUT_HEAD(qsort_test);
+ATF_TC_BODY(qsort_test, tc)
+{
+ int testvector[IVEC_LEN];
+ int sresvector[IVEC_LEN];
+ int i, j;
+
+ for (j = 2; j < IVEC_LEN; j++) {
+ /* Populate test vectors */
+ for (i = 0; i < j; i++)
+ testvector[i] = sresvector[i] = initvector[i];
+
+ /* Sort using qsort(3) */
+ qsort(testvector, j, sizeof(testvector[0]), sorthelp);
+ /* Sort using reference slow sorting routine */
+ ssort(sresvector, j);
+
+ /* Compare results */
+ for (i = 0; i < j; i++)
+ ATF_CHECK_MSG(testvector[i] == sresvector[i],
+ "item at index %d didn't match: %d != %d",
+ i, testvector[i], sresvector[i]);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, qsort_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/set_constraint_handler_s_test.c b/lib/libc/tests/stdlib/set_constraint_handler_s_test.c
new file mode 100644
index 000000000000..87db1f71ed4d
--- /dev/null
+++ b/lib/libc/tests/stdlib/set_constraint_handler_s_test.c
@@ -0,0 +1,60 @@
+/*-
+ * Copyright (c) 2017 Juniper Networks. 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <assert.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+/* null */
+ATF_TC_WITHOUT_HEAD(null_handler);
+ATF_TC_BODY(null_handler, tc)
+{
+ assert(set_constraint_handler_s(abort_handler_s) == NULL);
+}
+
+/* abort handler */
+ATF_TC_WITHOUT_HEAD(abort_handler);
+ATF_TC_BODY(abort_handler, tc)
+{
+ set_constraint_handler_s(abort_handler_s);
+ assert(set_constraint_handler_s(ignore_handler_s) == abort_handler_s);
+}
+
+/* ignore handler */
+ATF_TC_WITHOUT_HEAD(ignore_handler);
+ATF_TC_BODY(ignore_handler, tc)
+{
+ set_constraint_handler_s(ignore_handler_s);
+ assert(set_constraint_handler_s(abort_handler_s) == ignore_handler_s);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, null_handler);
+ ATF_TP_ADD_TC(tp, abort_handler);
+ ATF_TP_ADD_TC(tp, ignore_handler);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/strfmon_test.c b/lib/libc/tests/stdlib/strfmon_test.c
new file mode 100644
index 000000000000..86f6256dba0b
--- /dev/null
+++ b/lib/libc/tests/stdlib/strfmon_test.c
@@ -0,0 +1,252 @@
+/*-
+ * Copyright (C) 2018 Conrad Meyer <cem@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+
+#include <locale.h>
+#include <monetary.h>
+#include <stdio.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(strfmon_locale_thousands);
+ATF_TC_BODY(strfmon_locale_thousands, tc)
+{
+ char actual[40], expected[40];
+ struct lconv *lc;
+ const char *ts;
+ double n;
+
+ setlocale(LC_MONETARY, "sv_SE.UTF-8");
+
+ lc = localeconv();
+
+ ts = lc->mon_thousands_sep;
+ if (strlen(ts) == 0)
+ ts = lc->thousands_sep;
+
+ if (strlen(ts) < 2)
+ atf_tc_skip("multi-byte thousands-separator not found");
+
+ n = 1234.56;
+ strfmon(actual, sizeof(actual) - 1, "%i", n);
+
+ strcpy(expected, "1");
+ strlcat(expected, ts, sizeof(expected));
+ strlcat(expected, "234", sizeof(expected));
+
+ /* We're just testing the thousands separator, not all of strfmon. */
+ actual[strlen(expected)] = '\0';
+ ATF_CHECK_STREQ(expected, actual);
+}
+
+ATF_TC_WITHOUT_HEAD(strfmon_examples);
+ATF_TC_BODY(strfmon_examples, tc)
+{
+ const struct {
+ const char *format;
+ const char *expected;
+ } tests[] = {
+ { "%n", "[$123.45] [-$123.45] [$3,456.78]" },
+ { "%11n", "[ $123.45] [ -$123.45] [ $3,456.78]" },
+ { "%#5n", "[ $ 123.45] [-$ 123.45] [ $ 3,456.78]" },
+ { "%=*#5n", "[ $***123.45] [-$***123.45] [ $*3,456.78]" },
+ { "%=0#5n", "[ $000123.45] [-$000123.45] [ $03,456.78]" },
+ { "%^#5n", "[ $ 123.45] [-$ 123.45] [ $ 3456.78]" },
+ { "%^#5.0n", "[ $ 123] [-$ 123] [ $ 3457]" },
+ { "%^#5.4n", "[ $ 123.4500] [-$ 123.4500] [ $ 3456.7810]" },
+ { "%(#5n", "[ $ 123.45 ] [($ 123.45)] [ $ 3,456.78 ]" },
+ { "%!(#5n", "[ 123.45 ] [( 123.45)] [ 3,456.78 ]" },
+ { "%-14#5.4n", "[ $ 123.4500 ] [-$ 123.4500 ] [ $ 3,456.7810 ]" },
+ { "%14#5.4n", "[ $ 123.4500] [ -$ 123.4500] [ $ 3,456.7810]" },
+ };
+ size_t i;
+ char actual[100], format[50];
+
+ if (setlocale(LC_MONETARY, "en_US.UTF-8") == NULL)
+ atf_tc_skip("unable to setlocale()");
+
+ for (i = 0; i < nitems(tests); ++i) {
+ snprintf(format, sizeof(format), "[%s] [%s] [%s]",
+ tests[i].format, tests[i].format, tests[i].format);
+ strfmon(actual, sizeof(actual) - 1, format,
+ 123.45, -123.45, 3456.781);
+ ATF_CHECK_STREQ_MSG(tests[i].expected, actual,
+ "[%s]", tests[i].format);
+ }
+}
+
+ATF_TC(strfmon_cs_precedes_0);
+ATF_TC_HEAD(strfmon_cs_precedes_0, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "sep_by_space x sign_posn when cs_precedes = 0");
+}
+ATF_TC_BODY(strfmon_cs_precedes_0, tc)
+{
+ const struct {
+ const char *expected;
+ } tests[] = {
+ /* sep_by_space x sign_posn */
+ { "[(123.00$)] [-123.00$] [123.00$-] [123.00-$] [123.00$-]" },
+ { "[(123.00 $)] [-123.00 $] [123.00 $-] [123.00 -$] [123.00 $-]" },
+ { "[(123.00$)] [- 123.00$] [123.00$ -] [123.00- $] [123.00$ -]" },
+ };
+ size_t i, j;
+ struct lconv *lc;
+ char actual[100], buf[100];
+
+ if (setlocale(LC_MONETARY, "en_US.UTF-8") == NULL)
+ atf_tc_skip("unable to setlocale()");
+
+ lc = localeconv();
+ lc->n_cs_precedes = 0;
+
+ for (i = 0; i < nitems(tests); ++i) {
+ actual[0] = '\0';
+ lc->n_sep_by_space = i;
+
+ for (j = 0; j < 5; ++j) {
+ lc->n_sign_posn = j;
+
+ strfmon(buf, sizeof(buf) - 1, "[%n] ", -123.0);
+ strlcat(actual, buf, sizeof(actual));
+ }
+
+ actual[strlen(actual) - 1] = '\0';
+ ATF_CHECK_STREQ_MSG(tests[i].expected, actual,
+ "sep_by_space = %zu", i);
+ }
+}
+
+ATF_TC(strfmon_cs_precedes_1);
+ATF_TC_HEAD(strfmon_cs_precedes_1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "sep_by_space x sign_posn when cs_precedes = 1");
+}
+ATF_TC_BODY(strfmon_cs_precedes_1, tc)
+{
+ const struct {
+ const char *expected;
+ } tests[] = {
+ /* sep_by_space x sign_posn */
+ { "[($123.00)] [-$123.00] [$123.00-] [-$123.00] [$-123.00]" },
+ { "[($ 123.00)] [-$ 123.00] [$ 123.00-] [-$ 123.00] [$- 123.00]" },
+ { "[($123.00)] [- $123.00] [$123.00 -] [- $123.00] [$ -123.00]" },
+ };
+ size_t i, j;
+ struct lconv *lc;
+ char actual[100], buf[100];
+
+ if (setlocale(LC_MONETARY, "en_US.UTF-8") == NULL)
+ atf_tc_skip("unable to setlocale()");
+
+ lc = localeconv();
+ lc->n_cs_precedes = 1;
+
+ for (i = 0; i < nitems(tests); ++i) {
+ actual[0] = '\0';
+ lc->n_sep_by_space = i;
+
+ for (j = 0; j < 5; ++j) {
+ lc->n_sign_posn = j;
+
+ strfmon(buf, sizeof(buf) - 1, "[%n] ", -123.0);
+ strlcat(actual, buf, sizeof(actual));
+ }
+
+ actual[strlen(actual) - 1] = '\0';
+ ATF_CHECK_STREQ_MSG(tests[i].expected, actual,
+ "sep_by_space = %zu", i);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(strfmon_international_currency_code);
+ATF_TC_BODY(strfmon_international_currency_code, tc)
+{
+ const struct {
+ const char *locale;
+ const char *expected;
+ } tests[] = {
+ { "en_US.UTF-8", "[USD123.45]" },
+ { "de_DE.UTF-8", "[123,45 EUR]" },
+ { "C", "[123.45]" },
+ };
+ size_t i;
+ char actual[100];
+
+ for (i = 0; i < nitems(tests); ++i) {
+ if (setlocale(LC_MONETARY, tests[i].locale) == NULL)
+ atf_tc_skip("unable to setlocale()");
+
+ strfmon(actual, sizeof(actual) - 1, "[%i]", 123.45);
+ ATF_CHECK_STREQ(tests[i].expected, actual);
+ }
+}
+
+ATF_TC(strfmon_l);
+ATF_TC_HEAD(strfmon_l, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "checks strfmon_l under different locales");
+}
+ATF_TC_BODY(strfmon_l, 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[100];
+
+ for (i = 0; i < nitems(tests); ++i) {
+ loc = newlocale(LC_MONETARY_MASK, tests[i].locale, NULL);
+ ATF_REQUIRE(loc != NULL);
+
+ strfmon_l(buf, sizeof(buf) - 1, 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_locale_thousands);
+ ATF_TP_ADD_TC(tp, strfmon_examples);
+ ATF_TP_ADD_TC(tp, strfmon_cs_precedes_0);
+ ATF_TP_ADD_TC(tp, strfmon_cs_precedes_1);
+ ATF_TP_ADD_TC(tp, strfmon_international_currency_code);
+ ATF_TP_ADD_TC(tp, strfmon_l);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdlib/test-sort.h b/lib/libc/tests/stdlib/test-sort.h
new file mode 100644
index 000000000000..562f92b6748c
--- /dev/null
+++ b/lib/libc/tests/stdlib/test-sort.h
@@ -0,0 +1,271 @@
+/*-
+ * Copyright (C) 2004 Maxim Sobolev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _TEST_SORT_H
+#define _TEST_SORT_H
+
+#include <sys/param.h>
+
+#include <atf-c.h>
+
+static int
+sorthelp(const void *a, const void *b)
+{
+ const int *oa, *ob;
+
+ oa = a;
+ ob = b;
+ /* Don't use "return *oa - *ob" since it's easy to cause overflow! */
+ if (*oa > *ob)
+ return (1);
+ if (*oa < *ob)
+ return (-1);
+ return (0);
+}
+
+/* Reference sorting routine (slooow!) */
+static void
+ssort(int v[], int nmemb)
+{
+ int i, j, k;
+
+ for (i = 0; i < nmemb; i++) {
+ for (j = i + 1; j < nmemb; j++) {
+ if (v[j] < v[i]) {
+ k = v[i];
+ v[i] = v[j];
+ v[j] = k;
+ }
+ }
+ }
+}
+
+/* Some random data */
+static int initvector[1024] = {
+ 599853225, 371951333, -428880425, 1450668530, 85530178, -460170550,
+ -270680269, 717137591, 106244401, -1138072905, -113532586,
+ 599690958, 464993720, 107690177, 24249019, -1151018550, 794225588,
+ 1494905642, -62951716, -734531757, 1197251494, 1020063925,
+ -634536734, -216634704, 617366508, -18840190, 481010395,
+ -1555785724, 128221950, -1747743676, 1119012320, -252160437,
+ 617137855, 1159833073, -106647920, -1570719046, -769400990,
+ -130308848, 1186546396, 215253591, 445832488, 160263176, 777264170,
+ -274636650, -696134447, 1057957088, -626175254, 188632134,
+ -98941793, -1602334908, -373717125, 885013862, 571258221,
+ -399572192, 407761463, -733249776, 12094800, -367046815, 178974501,
+ -452638559, -516580143, -94282073, 1411115180, -1151792797,
+ 1442815268, 1106583674, 515269284, -74019972, 598818901, 696848474,
+ -657910766, -287309335, 1538095746, 143543003, 851444033,
+ -947573348, 296222973, 1339592140, -947505360, 872027891,
+ -418844714, -583075597, -324758197, -964531573, 1016795914,
+ -866842580, -304084402, -286930819, 338975583, 803597943,
+ -452483372, 1525283506, -1185830826, -596405894, 905191340,
+ -1638026278, 106749913, 582771197, -730713796, 56052422,
+ 1498040174, 644967266, 182541575, 280483468, -1932835017,
+ -435546874, 940106007, 1753388509, -340355019, -231577358,
+ -714879035, 1051182949, 204261785, 1321346243, -895289878,
+ -240369893, 566608506, -350777580, -1384849829, -876816409,
+ 1013787299, -1408035937, -222626650, 1708168486, -645148461,
+ 1854654, -393012507, 179327030, -1607975548, -715202732, 730588290,
+ 246968517, -550975254, -101136772, -174103176, 1188941016,
+ 2004650834, -1501389866, -2021225998, 1219042515, -464113898,
+ 268565342, 126451123, -1045877126, -198918003, 310177444,
+ 1578314812, 1828867082, 453336898, -908485523, 749777130,
+ -1028565802, 61360571, 241484853, -175693399, 1816211059,
+ 533940889, -1496933703, 385185125, -821132413, -8923391,
+ -1429546239, 46190992, 449247332, -20432494, 778761611, -178782699,
+ -811194695, -632276211, 70196094, 890817525, -1447776908,
+ -323783741, -62333222, 954929503, 247668507, -1394420605,
+ 367913886, 1364926759, 1298781924, 357923028, -73644747,
+ -319744305, 61718202, -1454549286, 387493522, 2018321449,
+ 861026653, 289973298, 1755939093, -84772204, 43425266, -1325753781,
+ -679938775, 1228500370, -763816405, 548903035, 1765563727,
+ 425183052, 1424621863, -188287424, 554385654, 751831998,
+ 1377088085, 66622090, 139224450, -1305816167, 650140724, 593988868,
+ -444913321, 589045097, 1261428989, 101292769, -780376786,
+ 559307348, 1644593236, -1838606833, 53570301, -680576100,
+ -44969538, 268718267, -632104125, 276904628, 626299449, -11761453,
+ 545899086, -1027343598, -432251872, 539353494, -399575006,
+ -568383580, -677802882, 1296513472, 801202048, 806425805, 1983363,
+ 850487421, 38046173, 1535399484, 1998285544, -1301751898,
+ -46561973, 56545408, -1107662676, 456041145, -452923904,
+ -262219453, -371849200, 392524281, 1650231392, 1185585356,
+ 126610511, -1125389657, 1015981177, -1318481342, -213835789,
+ 1263484054, -654361441, 1408272041, -231555284, -1121778152,
+ -395512897, 332277674, -349703586, 1809474399, -223731607,
+ -1342224737, 736165236, 67535587, 89879082, 471445838, 423788300,
+ -384354470, 907475706, 154886390, -1406736539, -8558766,
+ -203405718, -422127884, 1048520863, 747418501, 87438975, 727668160,
+ -914936981, 428266976, -455742009, -949014605, 487159058,
+ -262526850, -309451504, -76928461, 1072951428, -1370953830,
+ 238231681, -1831047244, -405735199, -35941848, 469048670,
+ 505307987, -660553590, 876537439, -314195871, 999234339,
+ -1405846240, -579885695, 629067031, 863481510, -742301385,
+ -1723403128, -153070386, 782682839, -343111818, -877101810,
+ 1438467302, -319951459, -1585702508, -338381888, 484108815,
+ -1726935191, -749923706, 1657932127, -816151395, -566321865,
+ -133542331, 84961164, 598275578, 1302065347, -533098653,
+ -1766383037, 53169714, -464201288, 617735133, 862536123,
+ -141730724, -1967377125, -1023736305, -1012511106, 64484992,
+ -1250856831, 426409332, -355670055, -298251663, -867634978,
+ -776124819, 74018333, -425385214, -108004251, -1192766130,
+ 1294193867, -109867195, -78667637, 1164855393, -826007552,
+ -522528066, 1160283046, -1095276403, -1218917472, -396475366,
+ -77394733, -425603169, 251928357, -393559355, 1488845184,
+ 898455978, -773338691, -37627855, -345014717, 204298578, 209084274,
+ 1047080643, -414348222, 26496664, 705759824, 575149152, 604904761,
+ 624988690, 1446041250, 1000757225, -1354896151, 1422958189,
+ -1607165311, -832757521, 365074225, 1171764999, 1200795217,
+ -203957828, 23166236, -846818698, -547439867, -790192498,
+ -122123561, 914298013, 66225525, -36163174, -480698856,
+ -1269421818, 624641703, 254923102, 1564329013, -583609221,
+ -649433551, -743396069, 1182455435, 102658744, 285599336,
+ 692480463, -852737563, -660090184, 1374350065, 72300985, 690541659,
+ -1194212957, -1151816525, 157415641, 487398246, 1030153072,
+ 933723020, 1359181027, -1303457764, -1543773272, 774306017,
+ -854382652, 755485667, -864943584, -63242545, -1271480354,
+ 157720215, -205001173, 889984130, -581583822, -473779111,
+ -932136577, 735326252, 428638717, 1888884222, 561493709,
+ -1966427364, -1710208603, 340121869, 918894036, 927172509,
+ 904178436, 1476687667, 90796414, 651591731, -550913123, -42218158,
+ 637756529, 1091478400, 124976584, -48739309, -798898083, 393581349,
+ -1078624722, -789291577, 1010582542, -512317005, 1222773656,
+ 466454593, 1183558811, 822782663, -851624703, -850404012,
+ 1473672600, 852527073, 1073953362, 137635828, 936104739, -86567759,
+ -882563252, 1845232980, -737978404, -104703380, 149802989,
+ -349118325, 1479797538, 1966007488, 1254228842, 414304661,
+ -790805785, 31583329, -76864151, 558202855, -1447047313, 716720607,
+ -404224238, -54107627, 1747385914, -657407390, 202940208,
+ 1083067056, -532861378, 163426933, -130289277, 1301785258,
+ -302920320, -637947485, -644895903, -1668027530, 391890467,
+ -126586485, -536836984, -1154536413, -339505118, -1187229462,
+ -670985870, -601310813, -1350055158, -482479784, 139847714,
+ -253955849, 5190414, -542857077, 1175835684, -584165057,
+ 1132775766, -592091269, -891445655, -340995936, 736395810,
+ 779967964, 515095845, -1138437307, -259226729, -167820100,
+ -801611617, -282451622, -1313679283, -1436126694, 1258773990,
+ -717601098, -583559645, -1307478759, 1238647247, 1589011223,
+ -1029216445, -107030650, 400152690, -1349079913, 1428582247,
+ 21546946, 208807686, -169450574, -1086213374, -1242715198,
+ 669098277, 416626722, -1122525014, -1389466669, -391843085,
+ -56318357, 421040094, 212303518, 738666684, -1185364904,
+ -506192760, 646735912, -1424722633, 745226976, 1019191717,
+ -190891744, -310320868, -373655022, -665117060, 830760000,
+ 583906512, -330536027, 611092636, -321344259, -1255008904,
+ -777959046, -523043472, 88896910, 346062204, -163734816, 336635665,
+ 906060268, -128891583, 740952564, 916767203, 296514859, 198670559,
+ 358136672, -152253676, 12038867, -1469916749, -1020980597,
+ -897143313, 354240970, -97959800, 814172811, 1983363241, 264990663,
+ 105154676, 1060463014, 430172143, 375111946, -824526943,
+ -708877751, -1377442616, -236559551, 684724584, 1602131358,
+ -42140186, -763499840, -605167, 98575879, -376577598, 1689746083,
+ -777973879, -1396984691, -187780326, 281040966, 1858859521,
+ 158395760, 1022807978, -218458812, 811779708, 1495113833,
+ 1192561226, 629539535, -1365196683, -1120253162, 1091681956,
+ 134286445, 1172218369, -34093658, -137087343, -27869226,
+ -180889478, 55705436, 52362743, -1745516232, -926564477,
+ -513701675, -1666272054, 1457952810, 843953275, -478275722,
+ -1240291080, 101859661, -1606687523, 916365333, 314713310,
+ -22002688, 1019022641, -1171741314, -129050627, -211955813,
+ -1020176299, 1357865293, -609819628, 724533854, -1141449545,
+ 22285231, -97731145, -302329687, 191910894, -1300709885,
+ -644951895, 640448036, -1289009824, 1445456129, 846416471,
+ 1821291740, -1639640821, -712724532, -447475807, 132156847,
+ 258067455, 324301319, 278586969, -1544082357, 636007277, 977122066,
+ 127462480, 365126129, 1086276825, -432124148, 896598926,
+ -388550179, 273357331, -845018695, -1089465389, 384439820,
+ -558419772, 1476422025, 730712769, 190665059, -764968927,
+ -789708218, 637873581, 348429858, 1386000168, -638123594,
+ -842010345, -607926448, 19535163, -742771490, -18323344, 246155488,
+ 350409468, 1290907730, -492566468, 300358636, 501876267, 252441807,
+ 1233551975, -1431067042, 517177374, -1540299707, -948380761,
+ 1253997663, 693795998, 148707823, 152894502, 98729538, -30042824,
+ -563057441, 723726409, 367338961, 1395435261, 217868876,
+ 1220652431, 953572083, -1134905155, -734486813, -587470130,
+ -864647866, 1030737023, 781798521, -321076732, -460548153,
+ 122681678, -873728847, -1286304399, -75472885, 113390881,
+ -1556849198, -1070802176, 924389470, 957478910, 5974049, 709413506,
+ 476334647, 572869787, 776298405, -8822420, -99326499, -707855342,
+ -1187216303, 668038414, 262454769, -1120293198, -32011040,
+ 249812104, -101835410, 1082281087, -570183855, -954535179,
+ 1384361473, -983199686, 2017611313, 328926483, -878162849,
+ -1202254181, -225604951, 966898955, 247213529, -1257544612,
+ -197005970, -1039902730, -1947925142, 1752780907, -313410699,
+ -464474556, 416580338, -1063356643, -292212702, 57509168,
+ 1034124629, 1059212593, 468522867, 845369497, 1872717306,
+ -1216544764, -1419907623, 1376869956, -66585488, -1590974467,
+ -367070705, -1456529060, 791844031, 336217635, -1106832215,
+ 1476739427, -751018210, -1411671555, -1880387375, -1714242595,
+ 1169006474, -479442380, -892267555, -1471250266, -267888858,
+ 808634418, 1324777189, -585449929, 1127186951, 468768901,
+ -2002989138, -52352849, 186493053, 1258464606, 117157186,
+ 445919258, 908401949, -1112221136, 863904453, -942718649,
+ 796754809, -38943966, -781978872, -56814078, 1273857459,
+ -1781920832, 209979504, 811828269, -1150814437, 850061883,
+ -532968763, 252506408, -885025629, 391567580, -1295702482,
+ 574894420, 1462248349, 1622584325, -88443443, -1122006092,
+ -169758578, 456282338, -443233309, 436693483, -956563815,
+ -480221349, 435252860, -1114099391, 1060053979, -470893945,
+ -1273682879, -734596176, 639950927, -1278648701, 306274747,
+ -410562146, 1137869228, -1970746553, 1313830798, 1248071822,
+ -247297704, 1015218053, -1607348116, -261817613, -821413148,
+ -782942639, -628711083, 240953091, -629550668, -1584736319,
+ 856616893, -186759752, 197905373, 541638393, 603407919, -278480495,
+ 410077039, 544065371, -1509627427, 402918436, -450463799,
+ -131169308, 249920630, 1079548609, -927911828, 1444577689,
+ -353002528, -224292462, 1247795279, -790844631, -40427503,
+ 59829765, -332587567, 1319862109, -1261340753, 121711726,
+ 1342756342, -643495787, 100326201, -934436714, -69134888,
+ -898880561, -379524737, -577234570, -805827092, -1575493557,
+ -289920678, -476605057, 1235714994, -317239591, 418553949,
+ 410053338, -204985448, 1206503615, 202610372, -932244873,
+ 782207875, -288661931, -806814809, 1270953679, 2060991595,
+ -311412846, 327279979, 1148562672, 55239149, -610114433,
+ -1511688434, 87619740, -433503545, 326150519, -581293393,
+ -97417688, -289736140, -1543886029, -1251976119, 1585774446,
+ 1570011421, 432602745, 486343696, -834680922, 265004849,
+ -1132107706, 502627197, -815873818, 249635389, 1985714127,
+ -1095817653, -130743522, -645266828, -334621094, 199921747,
+ 1059256177, 378031303, 1519740920, 925540689, 1405957844,
+ 1387748290, -56138046, -770637828, -187984510, -1361735163,
+ 1294823206, -608941238, 451860688, -510549867, 1143516283,
+ -779090703, 1459305047, -600335915, -1684168049, 1516622316,
+ -631733335, -4360068, 499778796, 587600402, -1296000335, -37959743,
+ -1084203927, 1162902556, 246374600, -515103645, 341724568,
+ -702303954, 452229900, 485108287, 1170048553, -1510148355,
+ 611133912, 1997927484, -142022671, -724776653, -336090522,
+ 708283514, -1409637378, -2052637687, 376055110, 226628105,
+ -1714452033, -1776158002, 369167930, 1800807012, 710680889,
+ -69951947, -1223849766, -1862239787, 218536127, -656411794,
+ -1202269188, 609634805, -224425858, 519797951, 284223080,
+ 869408930, 270750206, -544967439, 2097168510, 31650971, -600985417,
+ -165303097, -257809088, -1043223971, 1827891621, -156827355,
+ 499719603
+};
+
+#define IVEC_LEN (nitems(initvector))
+
+#endif
diff --git a/lib/libc/tests/stdlib/tsearch_test.c b/lib/libc/tests/stdlib/tsearch_test.c
new file mode 100644
index 000000000000..ee9deec588cb
--- /dev/null
+++ b/lib/libc/tests/stdlib/tsearch_test.c
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 2015 Nuxi, https://nuxi.nl/
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this 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 <atf-c.h>
+#define _SEARCH_PRIVATE
+#include <search.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+static int n_nodes = 0;
+static int n_seen = 0;
+
+/* Validates the integrity of an AVL tree. */
+static inline unsigned int
+tnode_assert(const posix_tnode *n)
+{
+ unsigned int height_left, height_right;
+ int balance;
+
+ if (n == NULL)
+ return 0;
+ height_left = tnode_assert(n->llink);
+ height_right = tnode_assert(n->rlink);
+ balance = (int)height_left - (int)height_right;
+ ATF_CHECK(balance >= -1);
+ ATF_CHECK(balance <= 1);
+ ATF_CHECK_EQ(balance, n->balance);
+ return (height_left > height_right ? height_left : height_right) + 1;
+}
+
+static int
+compar(const void *a, const void *b)
+{
+
+ return *(int *)a - *(int *)b;
+}
+
+static void
+treewalk(const posix_tnode *node, VISIT v, int level)
+{
+
+ if (v == postorder || v == leaf)
+ n_seen++;
+}
+
+ATF_TC_WITHOUT_HEAD(tsearch_test);
+ATF_TC_BODY(tsearch_test, tc)
+{
+ /*
+ * Run the test below in a deterministic fashion to prevent this
+ * test from potentially flapping. We assume that this provides
+ * enough coverage.
+ */
+#if 0
+ unsigned short random_state[3];
+ arc4random_buf(random_state, sizeof(random_state));
+#else
+ unsigned short random_state[3] = { 26554, 13330, 3246 };
+#endif
+
+#define NKEYS 1000
+ /* Create 1000 possible keys. */
+ int keys[NKEYS];
+ for (int i = 0; i < NKEYS; ++i)
+ keys[i] = i;
+
+ /* Apply random operations on a binary tree and check the results. */
+ posix_tnode *root = NULL;
+ bool present[NKEYS] = {};
+ for (int i = 0; i < NKEYS * 10; ++i) {
+ int key = nrand48(random_state) % NKEYS;
+ int sample = i;
+
+ /*
+ * Ensure each case is tested at least 10 times, plus a
+ * random sampling.
+ */
+ if ((sample % NKEYS) > 3)
+ sample = nrand48(random_state) % 3;
+
+ switch (sample) {
+ case 0: /* tdelete(). */
+ if (present[key]) {
+ ATF_CHECK(tdelete(&key, &root, compar) != NULL);
+ present[key] = false;
+ ATF_CHECK(n_nodes > 0);
+ n_nodes--;
+ } else {
+ ATF_CHECK_EQ(NULL,
+ tdelete(&key, &root, compar));
+ }
+ break;
+ case 1: /* tfind(). */
+ if (present[key]) {
+ ATF_CHECK_EQ(&keys[key],
+ *(int **)tfind(&key, &root, compar));
+ } else {
+ ATF_CHECK_EQ(NULL, tfind(&key, &root, compar));
+ }
+ break;
+ case 2: /* tsearch(). */
+ if (present[key]) {
+ ATF_CHECK_EQ(&keys[key],
+ *(int **)tsearch(&key, &root, compar));
+ } else {
+ ATF_CHECK_EQ(&keys[key], *(int **)tsearch(
+ &keys[key], &root, compar));
+ present[key] = true;
+ n_nodes++;
+ }
+ break;
+ }
+ tnode_assert(root);
+ }
+
+ /* Walk the tree. */
+ twalk(root, treewalk);
+ ATF_CHECK_EQ(n_nodes, n_seen);
+
+ /* Remove all entries from the tree. */
+ for (int key = 0; key < NKEYS; ++key)
+ if (present[key])
+ ATF_CHECK(tdelete(&key, &root, compar) != NULL);
+ ATF_CHECK_EQ(NULL, root);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, tsearch_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdtime/Makefile b/lib/libc/tests/stdtime/Makefile
new file mode 100644
index 000000000000..590dea22da31
--- /dev/null
+++ b/lib/libc/tests/stdtime/Makefile
@@ -0,0 +1,13 @@
+.include <src.opts.mk>
+
+ATF_TESTS_C+= strptime_test
+ATF_TESTS_C+= detect_tz_changes_test
+
+CFLAGS.detect_tz_changes_test+= -I${SRCTOP}/contrib/tzcode
+.if ${MK_DETECT_TZ_CHANGES} != "no"
+CFLAGS.detect_tz_changes_test+= -DDETECT_TZ_CHANGES
+.endif
+
+TESTSDIR:= ${TESTSBASE}/${RELDIR:C/libc\/tests/libc/}
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/stdtime/detect_tz_changes_test.c b/lib/libc/tests/stdtime/detect_tz_changes_test.c
new file mode 100644
index 000000000000..06c31c9fbc3d
--- /dev/null
+++ b/lib/libc/tests/stdtime/detect_tz_changes_test.c
@@ -0,0 +1,477 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "tzdir.h"
+
+#include <atf-c.h>
+
+struct tzcase {
+ const char *tzfn;
+ const char *expect;
+};
+
+static const struct tzcase tzcases[] = {
+ /*
+ * A handful of time zones and the expected result of
+ * strftime("%z (%Z)", tm) when that time zone is active
+ * and tm represents a date in the summer of 2025.
+ */
+ { "America/Vancouver", "-0700 (PDT)" },
+ { "America/New_York", "-0400 (EDT)" },
+ { "Europe/London", "+0100 (BST)" },
+ { "Europe/Paris", "+0200 (CEST)" },
+ { "Asia/Kolkata", "+0530 (IST)" },
+ { "Asia/Tokyo", "+0900 (JST)" },
+ { "Australia/Canberra", "+1000 (AEST)" },
+ { "UTC", "+0000 (UTC)" },
+ { 0 },
+};
+static const struct tzcase utc = { "UTC", "+0000 (UTC)" };
+static const struct tzcase invalid = { "invalid", "+0000 (-00)" };
+static const time_t then = 1751328000; /* 2025-07-01 00:00:00 UTC */
+
+static bool debugging;
+
+static void
+debug(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (debugging) {
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fputc('\n', stderr);
+ }
+}
+
+static void
+change_tz(const char *tzn)
+{
+ static const char *zfn = TZDIR;
+ static const char *tfn = "root" TZDEFAULT ".tmp";
+ static const char *dfn = "root" TZDEFAULT;
+ ssize_t clen;
+ int zfd, sfd, dfd;
+
+ ATF_REQUIRE((zfd = open(zfn, O_DIRECTORY | O_SEARCH)) >= 0);
+ ATF_REQUIRE((sfd = openat(zfd, tzn, O_RDONLY)) >= 0);
+ ATF_REQUIRE((dfd = open(tfn, O_CREAT | O_TRUNC | O_WRONLY, 0644)) >= 0);
+ do {
+ clen = copy_file_range(sfd, NULL, dfd, NULL, SSIZE_MAX, 0);
+ ATF_REQUIRE_MSG(clen != -1, "failed to copy %s/%s: %m",
+ zfn, tzn);
+ } while (clen > 0);
+ ATF_CHECK_EQ(0, close(dfd));
+ ATF_CHECK_EQ(0, close(sfd));
+ ATF_CHECK_EQ(0, close(zfd));
+ ATF_REQUIRE_EQ(0, rename(tfn, dfn));
+ debug("time zone %s installed", tzn);
+}
+
+static void
+test_tz(const char *expect)
+{
+ char buf[128];
+ struct tm *tm;
+ size_t len;
+
+ ATF_REQUIRE((tm = localtime(&then)) != NULL);
+ len = strftime(buf, sizeof(buf), "%z (%Z)", tm);
+ ATF_REQUIRE(len > 0);
+ ATF_CHECK_STREQ(expect, buf);
+}
+
+ATF_TC(tz_default);
+ATF_TC_HEAD(tz_default, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test default zone");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(tz_default, tc)
+{
+ /* prepare chroot with no /etc/localtime */
+ ATF_REQUIRE_EQ(0, mkdir("root", 0755));
+ ATF_REQUIRE_EQ(0, mkdir("root/etc", 0755));
+ /* enter chroot */
+ ATF_REQUIRE_EQ(0, chroot("root"));
+ ATF_REQUIRE_EQ(0, chdir("/"));
+ /* check timezone */
+ unsetenv("TZ");
+ test_tz("+0000 (UTC)");
+}
+
+ATF_TC(tz_invalid_file);
+ATF_TC_HEAD(tz_invalid_file, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test invalid zone file");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(tz_invalid_file, tc)
+{
+ static const char *dfn = "root/etc/localtime";
+ int fd;
+
+ /* prepare chroot with bogus /etc/localtime */
+ ATF_REQUIRE_EQ(0, mkdir("root", 0755));
+ ATF_REQUIRE_EQ(0, mkdir("root/etc", 0755));
+ ATF_REQUIRE((fd = open(dfn, O_RDWR | O_CREAT, 0644)) >= 0);
+ ATF_REQUIRE_EQ(8, write(fd, "invalid\n", 8));
+ ATF_REQUIRE_EQ(0, close(fd));
+ /* enter chroot */
+ ATF_REQUIRE_EQ(0, chroot("root"));
+ ATF_REQUIRE_EQ(0, chdir("/"));
+ /* check timezone */
+ unsetenv("TZ");
+ test_tz(invalid.expect);
+}
+
+ATF_TC(thin_jail);
+ATF_TC_HEAD(thin_jail, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test typical thin jail scenario");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(thin_jail, tc)
+{
+ const struct tzcase *tzcase = tzcases;
+
+ /* prepare chroot */
+ ATF_REQUIRE_EQ(0, mkdir("root", 0755));
+ ATF_REQUIRE_EQ(0, mkdir("root/etc", 0755));
+ change_tz(tzcase->tzfn);
+ /* enter chroot */
+ ATF_REQUIRE_EQ(0, chroot("root"));
+ ATF_REQUIRE_EQ(0, chdir("/"));
+ /* check timezone */
+ unsetenv("TZ");
+ test_tz(tzcase->expect);
+}
+
+#ifdef DETECT_TZ_CHANGES
+/*
+ * Test time zone change detection.
+ *
+ * The parent creates a chroot containing only /etc/localtime, initially
+ * set to UTC. It then forks a child which enters the chroot, repeatedly
+ * checks the current time zone, and prints it to stdout if it changes
+ * (including once on startup). Meanwhile, the parent waits for output
+ * from the child. Every time it receives a line of text from the child,
+ * it checks that it is as expected, then changes /etc/localtime within
+ * the chroot to the next case in the list. Once it reaches the end of
+ * the list, it closes a pipe to notify the child, which terminates.
+ *
+ * Note that ATF and / or Kyua may have set the timezone before the test
+ * case starts (even unintentionally). Therefore, we start the test only
+ * after we've received and discarded the first report from the child,
+ * which should come almost immediately on startup.
+ */
+static const char *tz_change_interval_sym = "__tz_change_interval";
+static int *tz_change_interval_p;
+static const int tz_change_interval = 3;
+static int tz_change_timeout = 90;
+
+ATF_TC(detect_tz_changes);
+ATF_TC_HEAD(detect_tz_changes, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test timezone change detection");
+ atf_tc_set_md_var(tc, "require.user", "root");
+ atf_tc_set_md_var(tc, "timeout", "600");
+}
+ATF_TC_BODY(detect_tz_changes, tc)
+{
+ char obuf[1024] = "";
+ char ebuf[1024] = "";
+ struct pollfd fds[3];
+ int opd[2], epd[2], spd[2];
+ time_t changed, now;
+ const struct tzcase *tzcase = NULL;
+ struct tm *tm;
+ size_t olen = 0, elen = 0;
+ ssize_t rlen;
+ long curoff = LONG_MIN;
+ pid_t pid;
+ int nfds, status;
+
+ /* speed up the test if possible */
+ tz_change_interval_p = dlsym(RTLD_SELF, tz_change_interval_sym);
+ if (tz_change_interval_p != NULL &&
+ *tz_change_interval_p > tz_change_interval) {
+ debug("reducing detection interval from %d to %d",
+ *tz_change_interval_p, tz_change_interval);
+ *tz_change_interval_p = tz_change_interval;
+ tz_change_timeout = tz_change_interval * 3;
+ }
+ /* prepare chroot */
+ ATF_REQUIRE_EQ(0, mkdir("root", 0755));
+ ATF_REQUIRE_EQ(0, mkdir("root/etc", 0755));
+ change_tz("UTC");
+ time(&changed);
+ /* output, error, sync pipes */
+ if (pipe(opd) != 0 || pipe(epd) != 0 || pipe(spd) != 0)
+ atf_tc_fail("failed to pipe");
+ /* fork child */
+ if ((pid = fork()) < 0)
+ atf_tc_fail("failed to fork");
+ if (pid == 0) {
+ /* child */
+ dup2(opd[1], STDOUT_FILENO);
+ close(opd[0]);
+ close(opd[1]);
+ dup2(epd[1], STDERR_FILENO);
+ close(epd[0]);
+ close(epd[1]);
+ close(spd[0]);
+ unsetenv("TZ");
+ ATF_REQUIRE_EQ(0, chroot("root"));
+ ATF_REQUIRE_EQ(0, chdir("/"));
+ fds[0].fd = spd[1];
+ fds[0].events = POLLIN;
+ for (;;) {
+ ATF_REQUIRE(poll(fds, 1, 100) >= 0);
+ if (fds[0].revents & POLLHUP) {
+ /* parent closed sync pipe */
+ _exit(0);
+ }
+ ATF_REQUIRE((tm = localtime(&then)) != NULL);
+ if (tm->tm_gmtoff == curoff)
+ continue;
+ olen = strftime(obuf, sizeof(obuf), "%z (%Z)", tm);
+ ATF_REQUIRE(olen > 0);
+ fprintf(stdout, "%s\n", obuf);
+ fflush(stdout);
+ curoff = tm->tm_gmtoff;
+ }
+ _exit(2);
+ }
+ /* parent */
+ close(opd[1]);
+ close(epd[1]);
+ close(spd[1]);
+ /* receive output until child terminates */
+ fds[0].fd = opd[0];
+ fds[0].events = POLLIN;
+ fds[1].fd = epd[0];
+ fds[1].events = POLLIN;
+ fds[2].fd = spd[0];
+ fds[2].events = POLLIN;
+ nfds = 3;
+ for (;;) {
+ ATF_REQUIRE(poll(fds, 3, 1000) >= 0);
+ time(&now);
+ if (fds[0].revents & POLLIN && olen < sizeof(obuf)) {
+ rlen = read(opd[0], obuf + olen, sizeof(obuf) - olen);
+ ATF_REQUIRE(rlen >= 0);
+ olen += rlen;
+ }
+ if (olen > 0) {
+ ATF_REQUIRE_EQ('\n', obuf[olen - 1]);
+ obuf[--olen] = '\0';
+ /* tzcase will be NULL at first */
+ if (tzcase != NULL) {
+ debug("%s", obuf);
+ ATF_REQUIRE_STREQ(tzcase->expect, obuf);
+ debug("change to %s detected after %d s",
+ tzcase->tzfn, (int)(now - changed));
+ if (tz_change_interval_p != NULL) {
+ ATF_CHECK((int)(now - changed) >=
+ *tz_change_interval_p - 1);
+ ATF_CHECK((int)(now - changed) <=
+ *tz_change_interval_p + 1);
+ }
+ }
+ olen = 0;
+ /* first / next test case */
+ if (tzcase == NULL)
+ tzcase = tzcases;
+ else
+ tzcase++;
+ if (tzcase->tzfn == NULL) {
+ /* test is over */
+ break;
+ }
+ change_tz(tzcase->tzfn);
+ changed = now;
+ }
+ if (fds[1].revents & POLLIN && elen < sizeof(ebuf)) {
+ rlen = read(epd[0], ebuf + elen, sizeof(ebuf) - elen);
+ ATF_REQUIRE(rlen >= 0);
+ elen += rlen;
+ }
+ if (elen > 0) {
+ ATF_REQUIRE_EQ(elen, fwrite(ebuf, 1, elen, stderr));
+ elen = 0;
+ }
+ if (nfds > 2 && fds[2].revents & POLLHUP) {
+ /* child closed sync pipe */
+ break;
+ }
+ /*
+ * The timeout for this test case is set to 10 minutes,
+ * because it can take that long to run with the default
+ * 61-second interval. However, each individual tzcase
+ * entry should not take much longer than the detection
+ * interval to test, so we can detect a problem long
+ * before Kyua terminates us.
+ */
+ if ((now - changed) > tz_change_timeout) {
+ close(spd[0]);
+ if (tz_change_interval_p == NULL &&
+ tzcase == tzcases) {
+ /*
+ * The most likely explanation in this
+ * case is that libc was built without
+ * time zone change detection.
+ */
+ atf_tc_skip("time zone change detection "
+ "does not appear to be enabled");
+ }
+ atf_tc_fail("timed out waiting for change to %s "
+ "to be detected", tzcase->tzfn);
+ }
+ }
+ close(opd[0]);
+ close(epd[0]);
+ close(spd[0]); /* this will wake up and terminate the child */
+ if (olen > 0)
+ ATF_REQUIRE_EQ(olen, fwrite(obuf, 1, olen, stdout));
+ if (elen > 0)
+ ATF_REQUIRE_EQ(elen, fwrite(ebuf, 1, elen, stderr));
+ ATF_REQUIRE_EQ(pid, waitpid(pid, &status, 0));
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_EQ(0, WEXITSTATUS(status));
+}
+#endif /* DETECT_TZ_CHANGES */
+
+static void
+test_tz_env(const char *tzval, const char *expect)
+{
+ setenv("TZ", tzval, 1);
+ test_tz(expect);
+}
+
+static void
+tz_env_common(void)
+{
+ char path[MAXPATHLEN];
+ const struct tzcase *tzcase = tzcases;
+ int len;
+
+ /* relative path */
+ for (tzcase = tzcases; tzcase->tzfn != NULL; tzcase++)
+ test_tz_env(tzcase->tzfn, tzcase->expect);
+ /* absolute path */
+ for (tzcase = tzcases; tzcase->tzfn != NULL; tzcase++) {
+ len = snprintf(path, sizeof(path), "%s/%s", TZDIR, tzcase->tzfn);
+ ATF_REQUIRE(len > 0 && (size_t)len < sizeof(path));
+ test_tz_env(path, tzcase->expect);
+ }
+ /* absolute path with additional slashes */
+ for (tzcase = tzcases; tzcase->tzfn != NULL; tzcase++) {
+ len = snprintf(path, sizeof(path), "%s/////%s", TZDIR, tzcase->tzfn);
+ ATF_REQUIRE(len > 0 && (size_t)len < sizeof(path));
+ test_tz_env(path, tzcase->expect);
+ }
+}
+
+ATF_TC(tz_env);
+ATF_TC_HEAD(tz_env, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test TZ environment variable");
+}
+ATF_TC_BODY(tz_env, tc)
+{
+ tz_env_common();
+ /* escape from TZDIR is permitted when not setugid */
+ test_tz_env("../zoneinfo/UTC", utc.expect);
+}
+
+
+ATF_TC(tz_invalid_env);
+ATF_TC_HEAD(tz_invalid_env, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test invalid TZ value");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(tz_invalid_env, tc)
+{
+ test_tz_env("invalid", invalid.expect);
+ test_tz_env(":invalid", invalid.expect);
+}
+
+ATF_TC(setugid);
+ATF_TC_HEAD(setugid, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test setugid process");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(setugid, tc)
+{
+ const struct tzcase *tzcase = tzcases;
+
+ /* prepare chroot */
+ ATF_REQUIRE_EQ(0, mkdir("root", 0755));
+ ATF_REQUIRE_EQ(0, mkdir("root/etc", 0755));
+ change_tz(tzcase->tzfn);
+ /* enter chroot */
+ ATF_REQUIRE_EQ(0, chroot("root"));
+ ATF_REQUIRE_EQ(0, chdir("/"));
+ /* become setugid */
+ ATF_REQUIRE_EQ(0, seteuid(UID_NOBODY));
+ ATF_REQUIRE(issetugid());
+ /* check timezone */
+ unsetenv("TZ");
+ test_tz(tzcases->expect);
+}
+
+ATF_TC(tz_env_setugid);
+ATF_TC_HEAD(tz_env_setugid, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test TZ environment variable "
+ "in setugid process");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(tz_env_setugid, tc)
+{
+ ATF_REQUIRE_EQ(0, seteuid(UID_NOBODY));
+ ATF_REQUIRE(issetugid());
+ tz_env_common();
+ /* escape from TZDIR is not permitted when setugid */
+ test_tz_env("../zoneinfo/UTC", invalid.expect);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ debugging = !getenv("__RUNNING_INSIDE_ATF_RUN") &&
+ isatty(STDERR_FILENO);
+ ATF_TP_ADD_TC(tp, tz_default);
+ ATF_TP_ADD_TC(tp, tz_invalid_file);
+ ATF_TP_ADD_TC(tp, thin_jail);
+#ifdef DETECT_TZ_CHANGES
+ ATF_TP_ADD_TC(tp, detect_tz_changes);
+#endif /* DETECT_TZ_CHANGES */
+ ATF_TP_ADD_TC(tp, tz_env);
+ ATF_TP_ADD_TC(tp, tz_invalid_env);
+ ATF_TP_ADD_TC(tp, setugid);
+ ATF_TP_ADD_TC(tp, tz_env_setugid);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/stdtime/strptime_test.c b/lib/libc/tests/stdtime/strptime_test.c
new file mode 100644
index 000000000000..79a97764999c
--- /dev/null
+++ b/lib/libc/tests/stdtime/strptime_test.c
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 2024 Dag-Erling Smørgrav
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <time.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(dayofweek);
+ATF_TC_BODY(dayofweek, tc)
+{
+ static const struct {
+ const char *str;
+ int wday;
+ } cases[] = {
+ { "1582-12-20", 1 },
+ { "1700-03-01", 1 },
+ { "1752-09-14", 4 },
+ { "1800-12-31", 3 },
+ { "1801-01-01", 4 },
+ { "1900-12-31", 1 },
+ { "1901-01-01", 2 },
+ { "2000-12-31", 0 },
+ { "2001-01-01", 1 },
+ { "2100-12-31", 5 },
+ { "2101-01-01", 6 },
+ { "2200-12-31", 3 },
+ { "2201-01-01", 4 },
+ { },
+ };
+ struct tm tm;
+
+ for (unsigned int i = 0; cases[i].str != NULL; i++) {
+ if (strptime(cases[i].str, "%Y-%m-%d", &tm) == NULL) {
+ atf_tc_fail_nonfatal("failed to parse %s",
+ cases[i].str);
+ } else if (tm.tm_wday != cases[i].wday) {
+ atf_tc_fail_nonfatal("expected %d for %s, got %d",
+ cases[i].wday, cases[i].str, tm.tm_wday);
+ }
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, dayofweek);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/string/Makefile b/lib/libc/tests/string/Makefile
new file mode 100644
index 000000000000..a019939c30af
--- /dev/null
+++ b/lib/libc/tests/string/Makefile
@@ -0,0 +1,58 @@
+# ensure libc functions are tested, not clang's builtins
+CFLAGS+= -fno-builtin
+
+ATF_TESTS_C+= bcmp_test
+ATF_TESTS_C+= ffs_test
+ATF_TESTS_C+= ffsl_test
+ATF_TESTS_C+= ffsll_test
+ATF_TESTS_C+= fls_test
+ATF_TESTS_C+= flsl_test
+ATF_TESTS_C+= flsll_test
+ATF_TESTS_C+= memccpy_test
+ATF_TESTS_C+= memcmp_test
+ATF_TESTS_C+= memrchr_test
+ATF_TESTS_C+= memset2_test
+ATF_TESTS_C+= memset_s_test
+ATF_TESTS_C+= strncmp_test
+ATF_TESTS_C+= stpncpy_test
+ATF_TESTS_C+= strnlen_test
+ATF_TESTS_C+= strcmp2_test
+ATF_TESTS_C+= strcspn_test
+ATF_TESTS_C+= strerror2_test
+ATF_TESTS_C+= strlcpy_test
+ATF_TESTS_C+= strspn_test
+ATF_TESTS_C+= strverscmp_test
+ATF_TESTS_C+= strxfrm_test
+ATF_TESTS_C+= timingsafe_bcmp_test
+ATF_TESTS_C+= timingsafe_memcmp_test
+ATF_TESTS_C+= wcscasecmp_test
+ATF_TESTS_C+= wcscoll_test
+ATF_TESTS_C+= wcsnlen_test
+
+# TODO: popcount, stresep
+
+NETBSD_ATF_TESTS_C+= memchr_test
+NETBSD_ATF_TESTS_C+= memcpy_test
+NETBSD_ATF_TESTS_C+= memmem_test
+NETBSD_ATF_TESTS_C+= memset_test
+NETBSD_ATF_TESTS_C+= strcat_test
+NETBSD_ATF_TESTS_C+= strchr_test
+NETBSD_ATF_TESTS_C+= strchrnul_test
+NETBSD_ATF_TESTS_C+= strcmp_test
+NETBSD_ATF_TESTS_C+= strcpy_test
+NETBSD_ATF_TESTS_C+= strerror_test
+NETBSD_ATF_TESTS_C+= strlen_test
+NETBSD_ATF_TESTS_C+= strpbrk_test
+NETBSD_ATF_TESTS_C+= strrchr_test
+NETBSD_ATF_TESTS_C+= swab_test
+
+SRCS.memset2_test= memset_test.c
+SRCS.strcmp2_test= strcmp_test.c
+SRCS.strerror2_test= strerror_test.c
+
+.include "../Makefile.netbsd-tests"
+
+LIBADD.memchr_test+= md
+LIBADD.memcpy_test+= md
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/string/Makefile.depend b/lib/libc/tests/string/Makefile.depend
new file mode 100644
index 000000000000..946944e9e951
--- /dev/null
+++ b/lib/libc/tests/string/Makefile.depend
@@ -0,0 +1,19 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libmd \
+ lib/libnetbsd \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/string/bcmp_test.c b/lib/libc/tests/string/bcmp_test.c
new file mode 100644
index 000000000000..fdf5e48b3eb4
--- /dev/null
+++ b/lib/libc/tests/string/bcmp_test.c
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Robert Clausecker <fuz@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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
+ */
+
+#define MEMCMP bcmp
+#define RES(x) ((x) != 0)
+
+#include "memcmp_test.c"
diff --git a/lib/libc/tests/string/ffs_test.c b/lib/libc/tests/string/ffs_test.c
new file mode 100644
index 000000000000..4b59385d712e
--- /dev/null
+++ b/lib/libc/tests/string/ffs_test.c
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Robert Clausecker <fuz@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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 <atf-c.h>
+#include <limits.h>
+#include <stdint.h>
+#include <strings.h>
+
+#ifndef FFS
+# define FFS ffs
+# define TYPE int
+# define TYPE_MIN INT_MIN
+#endif
+
+ATF_TC_WITHOUT_HEAD(zero);
+ATF_TC_BODY(zero, tc)
+{
+ ATF_CHECK_EQ((TYPE)0, FFS(0));
+}
+
+ATF_TC_WITHOUT_HEAD(twobit);
+ATF_TC_BODY(twobit, tc)
+{
+ const TYPE one = 1;
+ TYPE x;
+ const int n = sizeof(TYPE) * CHAR_BIT;
+ int i, j;
+
+ for (i = 0; i < n - 1; i++)
+ for (j = 0; j <= i; j++) {
+ x = one << i | one << j;
+ ATF_CHECK_EQ_MSG(j + 1, FFS(x),
+ "%s(%#jx) == %d != %d", __STRING(FFS), (intmax_t)x, FFS(x), j + 1);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(twobitneg);
+ATF_TC_BODY(twobitneg, tc)
+{
+ const TYPE one = 1;
+ TYPE x;
+ const int n = sizeof(TYPE) * CHAR_BIT;
+ int i, j;
+
+ for (i = 0; i < n - 1; i++)
+ for (j = 0; j <= i; j++) {
+ x = one << i | one << j | TYPE_MIN;
+ ATF_CHECK_EQ_MSG(j + 1, FFS(x),
+ "%s(%#jx) == %d != %d", __STRING(FFS), (intmax_t)x, FFS(x), j + 1);
+ }
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, zero);
+ ATF_TP_ADD_TC(tp, twobit);
+ ATF_TP_ADD_TC(tp, twobitneg);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/string/ffsl_test.c b/lib/libc/tests/string/ffsl_test.c
new file mode 100644
index 000000000000..809cea3d3a93
--- /dev/null
+++ b/lib/libc/tests/string/ffsl_test.c
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Robert Clausecker <fuz@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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
+ */
+#define FFS ffsl
+#define TYPE long
+#define TYPE_MIN LONG_MIN
+
+#include "ffs_test.c"
diff --git a/lib/libc/tests/string/ffsll_test.c b/lib/libc/tests/string/ffsll_test.c
new file mode 100644
index 000000000000..1a620cbf9aba
--- /dev/null
+++ b/lib/libc/tests/string/ffsll_test.c
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Robert Clausecker <fuz@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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
+ */
+#define FFS ffsll
+#define TYPE long long
+#define TYPE_MIN LLONG_MIN
+
+#include "ffs_test.c"
diff --git a/lib/libc/tests/string/fls_test.c b/lib/libc/tests/string/fls_test.c
new file mode 100644
index 000000000000..55691b36617c
--- /dev/null
+++ b/lib/libc/tests/string/fls_test.c
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Robert Clausecker <fuz@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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 <atf-c.h>
+#include <limits.h>
+#include <stdint.h>
+#include <strings.h>
+
+#ifndef FLS
+# define FLS fls
+# define TYPE int
+# define TYPE_MIN INT_MIN
+#endif
+
+ATF_TC_WITHOUT_HEAD(zero);
+ATF_TC_BODY(zero, tc)
+{
+ ATF_CHECK_EQ((TYPE)0, FLS(0));
+}
+
+ATF_TC_WITHOUT_HEAD(twobit);
+ATF_TC_BODY(twobit, tc)
+{
+ const TYPE one = 1;
+ TYPE x;
+ const int n = sizeof(TYPE) * CHAR_BIT;
+ int i, j;
+
+ for (i = 0; i < n - 1; i++)
+ for (j = 0; j <= i; j++) {
+ x = one << i | one << j;
+ ATF_CHECK_EQ_MSG(i + 1, FLS(x),
+ "%s(%#jx) == %d != %d", __STRING(FLS), (intmax_t)x, FLS(x), i + 1);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(twobitneg);
+ATF_TC_BODY(twobitneg, tc)
+{
+ const TYPE one = 1;
+ TYPE x;
+ const int n = sizeof(TYPE) * CHAR_BIT;
+ int i, j;
+
+ for (i = 0; i < n - 1; i++)
+ for (j = 0; j <= i; j++) {
+ x = one << i | one << j | TYPE_MIN;
+ ATF_CHECK_EQ_MSG(n, FLS(x),
+ "%s(%#jx) == %d != %d", __STRING(FLS), (intmax_t)x, FLS(x), n);
+ }
+}
+
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, zero);
+ ATF_TP_ADD_TC(tp, twobit);
+ ATF_TP_ADD_TC(tp, twobitneg);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/string/flsl_test.c b/lib/libc/tests/string/flsl_test.c
new file mode 100644
index 000000000000..4b1e330d4564
--- /dev/null
+++ b/lib/libc/tests/string/flsl_test.c
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Robert Clausecker <fuz@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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
+ */
+#define FLS flsl
+#define TYPE long
+#define TYPE_MIN LONG_MIN
+
+#include "fls_test.c"
diff --git a/lib/libc/tests/string/flsll_test.c b/lib/libc/tests/string/flsll_test.c
new file mode 100644
index 000000000000..23f469b7b5de
--- /dev/null
+++ b/lib/libc/tests/string/flsll_test.c
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Robert Clausecker <fuz@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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
+ */
+#define FLS flsll
+#define TYPE long long
+#define TYPE_MIN LLONG_MIN
+
+#include "fls_test.c"
diff --git a/lib/libc/tests/string/memccpy_test.c b/lib/libc/tests/string/memccpy_test.c
new file mode 100644
index 000000000000..4784fee4ede5
--- /dev/null
+++ b/lib/libc/tests/string/memccpy_test.c
@@ -0,0 +1,229 @@
+/*-
+ * Copyright (c) 2009 David Schultz <das@FreeBSD.org>
+ * Copyright (c) 2023, 2024 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Robert Clausecker
+ * <fuz@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <dlfcn.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+void *(*memccpy_fn)(void *restrict, const void *restrict, int, size_t);
+
+static char *
+makebuf(size_t len, int guard_at_end)
+{
+ char *buf;
+ size_t alloc_size, page_size;
+
+ page_size = getpagesize();
+ alloc_size = roundup2(len, page_size) + page_size;
+
+ buf = mmap(NULL, alloc_size, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
+ assert(buf);
+ if (guard_at_end) {
+ assert(mprotect(buf + alloc_size - page_size, page_size, PROT_NONE) == 0);
+ return (buf + alloc_size - page_size - len);
+ } else {
+ assert(mprotect(buf, page_size, PROT_NONE) == 0);
+ return (buf + page_size);
+ }
+}
+
+static void
+freebuf(char * buf, size_t len, int guard_at_end)
+{
+ size_t alloc_size, page_size;
+
+ page_size = getpagesize();
+ alloc_size = roundup2(len, page_size) + page_size;
+
+ if (guard_at_end)
+ munmap(buf + len + page_size - alloc_size, alloc_size);
+ else
+ munmap(buf - page_size, alloc_size);
+}
+
+static void
+test_memccpy(const char *s, size_t size)
+{
+ char *src, *dst, *expected;
+ size_t bufsize, x;
+ int i, j;
+
+ for (i = 0; i <= 1; i++) {
+ for (j = 0; j <= 1; j++) {
+ for (bufsize = 0; bufsize <= size + 32; bufsize++) {
+ dst = makebuf(bufsize, j);
+ if (bufsize < size) {
+ src = makebuf(bufsize, i);
+ memcpy(src, s, bufsize);
+ expected = NULL;
+ } else {
+ src = makebuf(size, i);
+ memcpy(src, s, size);
+ expected = dst + size;
+ }
+
+ memset(dst, 'X', bufsize);
+ assert(memccpy_fn(dst, src, s[size-1], bufsize) == expected);
+ assert(memcmp(src, dst, MIN(bufsize, size)) == 0);
+ for (x = size; x < bufsize; x++)
+ assert(dst[x] == 'X');
+
+ freebuf(dst, bufsize, j);
+ freebuf(src, bufsize < size ? bufsize : size, i);
+ }
+ }
+ }
+}
+
+static void
+test_sentinel(char *dest, char *src, size_t destlen, size_t srclen)
+{
+ size_t i, effective_len;
+ void *res, *wantres;
+ const char *fail = NULL;
+ char terminator;
+
+ for (i = 0; i < srclen; i++)
+ /* src will never include (){} */
+ src[i] = '0' + i;
+
+ /* source sentinels: not to be copied */
+ src[-1] = '(';
+ src[srclen] = ')';
+
+ memset(dest, '\xee', destlen);
+
+ /* destination sentinels: not to be touched */
+ dest[-1] = '{';
+ dest[destlen] = '}';
+
+ effective_len = srclen < destlen ? srclen : destlen;
+ wantres = srclen <= destlen ? dest + srclen : NULL;
+ terminator = src[srclen-1];
+ res = memccpy_fn(dest, src, terminator, destlen);
+
+ if (dest[-1] != '{')
+ fail = "start sentinel overwritten";
+ else if (dest[destlen] != '}')
+ fail = "end sentinel overwritten";
+ else if (res != wantres)
+ fail = "incorrect return value";
+ else if (destlen > 0 && memcmp(src, dest, effective_len) != 0)
+ fail = "string not copied correctly";
+ else for (i = srclen; i < destlen; i++)
+ if (dest[i] != '\xee') {
+ fail = "buffer mutilated behind string";
+ break;
+ }
+
+ if (fail)
+ atf_tc_fail_nonfatal("%s\n"
+ "memccpy(%p \"%s\", %p \"%s\", %u '%c', %zu) = %p (want %p)\n",
+ fail, dest, dest, src, src, terminator, terminator, destlen, res, wantres);
+}
+
+ATF_TC_WITHOUT_HEAD(null);
+ATF_TC_BODY(null, tc)
+{
+ ATF_CHECK_EQ(memccpy_fn(NULL, "foo", 42, 0), NULL);
+}
+
+ATF_TC(zero_extension);
+ATF_TC_HEAD(zero_extension, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Ensure the upper bits of the terminator are ignored");
+}
+ATF_TC_BODY(zero_extension, tc)
+{
+ int mask = -1 & ~UCHAR_MAX;
+ char buf[16];
+
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ(memccpy(buf, "foobar", 'r', sizeof(buf)), buf + sizeof("foobar") - 1);
+ ATF_CHECK_EQ(memcmp(buf, "foobar", sizeof("foobar") - 1), 0);
+
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ(memccpy(buf, "foobar", mask | 'r', sizeof(buf)), buf + sizeof("foobar") - 1);
+ ATF_CHECK_EQ(memcmp(buf, "foobar", sizeof("foobar") - 1), 0);
+}
+
+ATF_TC_WITHOUT_HEAD(bounds);
+ATF_TC_BODY(bounds, tc)
+{
+ size_t i;
+ char buf[64];
+
+ for (i = 0; i < sizeof(buf) - 1; i++) {
+ buf[i] = ' ' + i;
+ buf[i+1] = '\0';
+ test_memccpy(buf, i + 1);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(alignments);
+ATF_TC_BODY(alignments, tc)
+{
+ size_t srcalign, destalign, srclen, destlen;
+ char src[15+2+64]; /* 15 offsets + 64 max length + sentinels */
+ char dest[15+2+64]; /* 15 offsets + 64 max length + sentinels */
+
+ for (srcalign = 0; srcalign < 16; srcalign++)
+ for (destalign = 0; destalign < 16; destalign++)
+ for (srclen = 1; srclen < 64; srclen++)
+ for (destlen = 0; destlen < 64; destlen++)
+ test_sentinel(dest+destalign+1,
+ src+srcalign+1, destlen, srclen);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ void *dl_handle;
+
+ dl_handle = dlopen(NULL, RTLD_LAZY);
+ memccpy_fn = dlsym(dl_handle, "test_memccpy");
+ if (memccpy_fn == NULL)
+ memccpy_fn = memccpy;
+
+ ATF_TP_ADD_TC(tp, null);
+ ATF_TP_ADD_TC(tp, zero_extension);
+ ATF_TP_ADD_TC(tp, bounds);
+ ATF_TP_ADD_TC(tp, alignments);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/string/memcmp_test.c b/lib/libc/tests/string/memcmp_test.c
new file mode 100644
index 000000000000..fa2f498ccfaf
--- /dev/null
+++ b/lib/libc/tests/string/memcmp_test.c
@@ -0,0 +1,160 @@
+/*-
+ * Copyright (c) 2016 Jilles Tjoelker <jilles@FreeBSD.org>
+ * Copyright (c) 2023 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Robert Clausecker
+ * <fuz@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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 <assert.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#ifndef MEMCMP
+#define MEMCMP memcmp
+#endif
+
+/*
+ * On FreeBSD we previously demanded that memcmp returns the difference
+ * between the characters at the first site of mismatch. However,
+ * ISO/IEC 9899:1990 only specifies that a number greater than, equal
+ * to, or less than zero shall be returned. If a unit test for the
+ * more strict behaviour is desired, define RES(x) to be (x).
+ */
+#ifndef RES
+#define RES(x) (((x) > 0) - ((x) < 0))
+#endif
+
+static int (*memcmp_fn)(const void *, const void *, size_t);
+
+static void
+check_memcmp(const char *a, const char *b, size_t len, int expected)
+{
+ int got;
+
+ got = memcmp_fn(a, b, len);
+ ATF_CHECK_EQ_MSG(RES(expected), RES(got),
+ "%s(%p, %p, %zu) gave %d, but wanted %d",
+ __XSTRING(MEMCMP), a, b, len, got, expected);
+}
+
+ATF_TC_WITHOUT_HEAD(zero);
+ATF_TC_BODY(zero, tc)
+{
+
+ check_memcmp("a", "b", 0, 0);
+ check_memcmp("", "", 0, 0);
+}
+
+ATF_TC_WITHOUT_HEAD(eq);
+ATF_TC_BODY(eq, tc)
+{
+ unsigned char data1[256], data2[256];
+ int i;
+
+ for (i = 0; i < 256; i++)
+ data1[i] = data2[i] = i ^ 0x55;
+ for (i = 1; i < 256; i++)
+ check_memcmp(data1, data2, i, 0);
+ for (i = 1; i < 256; i++)
+ check_memcmp(data1 + i, data2 + i, 256 - i, 0);
+}
+
+ATF_TC_WITHOUT_HEAD(neq);
+ATF_TC_BODY(neq, tc)
+{
+ unsigned char data1[256], data2[256];
+ int i;
+
+ for (i = 0; i < 256; i++) {
+ data1[i] = i;
+ data2[i] = i ^ 0x55;
+ }
+ for (i = 1; i < 256; i++)
+ check_memcmp(data1, data2, i, -0x55);
+ for (i = 1; i < 256; i++)
+ check_memcmp(data1 + i, data2 + i, 256 - i, i - (i ^ 0x55));
+}
+
+ATF_TC_WITHOUT_HEAD(diff);
+ATF_TC_BODY(diff, tc)
+{
+ unsigned char data1[256], data2[256];
+ int i;
+
+ memset(data1, 'a', sizeof(data1));
+ memset(data2, 'a', sizeof(data2));
+ data1[128] = 255;
+ data2[128] = 0;
+ for (i = 1; i < 66; i++) {
+ check_memcmp(data1 + 128, data2 + 128, i, 255);
+ check_memcmp(data2 + 128, data1 + 128, i, -255);
+ check_memcmp(data1 + 129 - i, data2 + 129 - i, i, 255);
+ check_memcmp(data2 + 129 - i, data1 + 129 - i, i, -255);
+ check_memcmp(data1 + 129 - i, data2 + 129 - i, i * 2, 255);
+ check_memcmp(data2 + 129 - i, data1 + 129 - i, i * 2, -255);
+ }
+ data1[128] = 'c';
+ data2[128] = 'e';
+ for (i = 1; i < 66; i++) {
+ check_memcmp(data1 + 128, data2 + 128, i, -2);
+ check_memcmp(data2 + 128, data1 + 128, i, 2);
+ check_memcmp(data1 + 129 - i, data2 + 129 - i, i, -2);
+ check_memcmp(data2 + 129 - i, data1 + 129 - i, i, 2);
+ check_memcmp(data1 + 129 - i, data2 + 129 - i, i * 2, -2);
+ check_memcmp(data2 + 129 - i, data1 + 129 - i, i * 2, 2);
+ }
+ memset(data1 + 129, 'A', sizeof(data1) - 129);
+ memset(data2 + 129, 'Z', sizeof(data2) - 129);
+ for (i = 1; i < 66; i++) {
+ check_memcmp(data1 + 128, data2 + 128, i, -2);
+ check_memcmp(data2 + 128, data1 + 128, i, 2);
+ check_memcmp(data1 + 129 - i, data2 + 129 - i, i, -2);
+ check_memcmp(data2 + 129 - i, data1 + 129 - i, i, 2);
+ check_memcmp(data1 + 129 - i, data2 + 129 - i, i * 2, -2);
+ check_memcmp(data2 + 129 - i, data1 + 129 - i, i * 2, 2);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ void *dl_handle;
+
+ dl_handle = dlopen(NULL, RTLD_LAZY);
+ memcmp_fn = dlsym(dl_handle, "test_" __XSTRING(MEMCMP));
+ if (memcmp_fn == NULL)
+ memcmp_fn = MEMCMP;
+
+ ATF_TP_ADD_TC(tp, zero);
+ ATF_TP_ADD_TC(tp, eq);
+ ATF_TP_ADD_TC(tp, neq);
+ ATF_TP_ADD_TC(tp, diff);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/string/memrchr_test.c b/lib/libc/tests/string/memrchr_test.c
new file mode 100644
index 000000000000..12f696c9dc1e
--- /dev/null
+++ b/lib/libc/tests/string/memrchr_test.c
@@ -0,0 +1,116 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Robert Clausecker
+ */
+
+#include <sys/cdefs.h>
+
+#include <dlfcn.h>
+#include <limits.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+static void *(*memrchr_fn)(const void *, int, size_t);
+
+ATF_TC_WITHOUT_HEAD(null);
+ATF_TC_BODY(null, tc)
+{
+ ATF_CHECK_EQ(memrchr_fn(NULL, 42, 0), NULL);
+}
+
+ATF_TC_WITHOUT_HEAD(not_found);
+ATF_TC_BODY(not_found, tc)
+{
+ size_t i, j;
+ char buf[1+15+64+1]; /* offset [0..15] + 64 buffer bytes + sentinels */
+
+ buf[0] = 'X';
+ memset(buf + 1, '-', sizeof(buf) - 1);
+
+ for (i = 0; i < 16; i++)
+ for (j = 0; j < 64; j++) {
+ buf[i + j + 1] = 'X';
+ ATF_CHECK_EQ(memrchr_fn(buf + i + 1, 'X', j), NULL);
+ buf[i + j + 1] = '-';
+ }
+}
+
+static void
+do_found_test(char buf[], size_t len, size_t first, size_t second)
+{
+ /* invariant: first <= second */
+
+ buf[first] = 'X';
+ buf[second] = 'X';
+ ATF_CHECK_EQ(memrchr_fn(buf, 'X', len), buf + second);
+ buf[first] = '-';
+ buf[second] = '-';
+}
+
+ATF_TC_WITHOUT_HEAD(found);
+ATF_TC_BODY(found, tc)
+{
+ size_t i, j, k, l;
+ char buf[1+15+64+1];
+
+ buf[0] = 'X';
+ memset(buf + 1, '-', sizeof(buf) - 1);
+
+ for (i = 0; i < 16; i++)
+ for (j = 0; j < 64; j++)
+ for (k = 0; k < j; k++)
+ for (l = 0; l <= k; l++) {
+ buf[i + j + 1] = 'X';
+ do_found_test(buf + i + 1, j, l, k);
+ buf[i + j + 1] = '-';
+ }
+}
+
+/* check that the right character is found */
+static void
+do_values_test(unsigned char buf[], size_t len, size_t i, int c)
+{
+ /* sentinels */
+ buf[-1] = c;
+ buf[len] = c;
+ memset(buf, c + 1, len);
+
+ if (i < len) {
+ buf[i] = c;
+ ATF_CHECK_EQ(memrchr_fn(buf, c, len), buf + i);
+ } else
+ ATF_CHECK_EQ(memrchr_fn(buf, c, len), NULL);
+}
+
+ATF_TC_WITHOUT_HEAD(values);
+ATF_TC_BODY(values, tc)
+{
+ size_t i, j, k;
+ int c;
+ unsigned char buf[1+15+64+1];
+
+ for (i = 0; i < 16; i++)
+ for (j = 0; j < 64; j++)
+ for (k = 0; k <= j; k++)
+ for (c = 0; c <= UCHAR_MAX; c++)
+ do_values_test(buf + i + 1, j, k, c);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ void *dl_handle;
+
+ dl_handle = dlopen(NULL, RTLD_LAZY);
+ memrchr_fn = dlsym(dl_handle, "test_memrchr");
+ if (memrchr_fn == NULL)
+ memrchr_fn = memrchr;
+
+ ATF_TP_ADD_TC(tp, null);
+ ATF_TP_ADD_TC(tp, not_found);
+ ATF_TP_ADD_TC(tp, found);
+ ATF_TP_ADD_TC(tp, values);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/string/memset_s_test.c b/lib/libc/tests/string/memset_s_test.c
new file mode 100644
index 000000000000..c822a2ee4554
--- /dev/null
+++ b/lib/libc/tests/string/memset_s_test.c
@@ -0,0 +1,197 @@
+/*-
+ * Copyright (c) 2017 Juniper Networks. 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+static errno_t e;
+static const char * restrict m;
+
+void
+h(const char * restrict msg, void * restrict ptr __unused, errno_t error)
+{
+ e = error;
+ m = msg;
+}
+
+/* null ptr */
+ATF_TC_WITHOUT_HEAD(null_ptr);
+ATF_TC_BODY(null_ptr, tc)
+{
+ assert(memset_s(0, 1, 1, 1) != 0);
+}
+
+/* smax > rmax */
+ATF_TC_WITHOUT_HEAD(smax_gt_rmax);
+ATF_TC_BODY(smax_gt_rmax, tc)
+{
+ char b;
+
+ assert(memset_s(&b, RSIZE_MAX + 1, 1, 1) != 0);
+}
+
+/* smax < 0 */
+ATF_TC_WITHOUT_HEAD(smax_lt_zero);
+ATF_TC_BODY(smax_lt_zero, tc)
+{
+ char b;
+
+ assert(memset_s(&b, -1, 1, 1) != 0);
+}
+
+/* normal */
+ATF_TC_WITHOUT_HEAD(normal);
+ATF_TC_BODY(normal, tc)
+{
+ char b;
+
+ b = 3;
+ assert(memset_s(&b, 1, 5, 1) == 0);
+ assert(b == 5);
+}
+
+/* n > rmax */
+ATF_TC_WITHOUT_HEAD(n_gt_rmax);
+ATF_TC_BODY(n_gt_rmax, tc)
+{
+ char b;
+
+ assert(memset_s(&b, 1, 1, RSIZE_MAX + 1) != 0);
+}
+
+/* n < 0 */
+ATF_TC_WITHOUT_HEAD(n_lt_zero);
+ATF_TC_BODY(n_lt_zero, tc)
+{
+ char b;
+
+ assert(memset_s(&b, 1, 1, -1) != 0);
+}
+
+/* n < smax */
+ATF_TC_WITHOUT_HEAD(n_lt_smax);
+ATF_TC_BODY(n_lt_smax, tc)
+{
+ char b[3] = {1, 2, 3};
+
+ assert(memset_s(&b[0], 3, 9, 1) == 0);
+ assert(b[0] == 9);
+ assert(b[1] == 2);
+ assert(b[2] == 3);
+}
+
+/* n > smax, handler */
+ATF_TC_WITHOUT_HEAD(n_gt_smax);
+ATF_TC_BODY(n_gt_smax, tc)
+{
+ char b[3] = {1, 2, 3};
+
+ e = 0;
+ m = NULL;
+ set_constraint_handler_s(h);
+ assert(memset_s(&b[0], 1, 9, 3) != 0);
+ assert(e > 0);
+ assert(strcmp(m, "memset_s : n > smax") == 0);
+ assert(b[0] == 9);
+ assert(b[1] == 2);
+ assert(b[2] == 3);
+}
+
+/* smax > rmax, handler */
+ATF_TC_WITHOUT_HEAD(smax_gt_rmax_handler);
+ATF_TC_BODY(smax_gt_rmax_handler, tc)
+{
+ char b;
+
+ e = 0;
+ m = NULL;
+ set_constraint_handler_s(h);
+ assert(memset_s(&b, RSIZE_MAX + 1, 1, 1) != 0);
+ assert(e > 0);
+ assert(strcmp(m, "memset_s : smax > RSIZE_MAX") == 0);
+}
+
+/* smax < 0, handler */
+ATF_TC_WITHOUT_HEAD(smax_lt_zero_handler);
+ATF_TC_BODY(smax_lt_zero_handler, tc)
+{
+ char b;
+
+ e = 0;
+ m = NULL;
+ set_constraint_handler_s(h);
+ assert(memset_s(&b, -1, 1, 1) != 0);
+ assert(e > 0);
+ assert(strcmp(m, "memset_s : smax > RSIZE_MAX") == 0);
+}
+
+/* n > rmax, handler */
+ATF_TC_WITHOUT_HEAD(n_gt_rmax_handler);
+ATF_TC_BODY(n_gt_rmax_handler, tc)
+{
+ char b;
+
+ e = 0;
+ m = NULL;
+ set_constraint_handler_s(h);
+ assert(memset_s(&b, 1, 1, RSIZE_MAX + 1) != 0);
+ assert(e > 0);
+ assert(strcmp(m, "memset_s : n > RSIZE_MAX") == 0);
+}
+
+/* n < 0, handler */
+ATF_TC_WITHOUT_HEAD(n_lt_zero_handler);
+ATF_TC_BODY(n_lt_zero_handler, tc)
+{
+ char b;
+
+ e = 0;
+ m = NULL;
+ set_constraint_handler_s(h);
+ assert(memset_s(&b, 1, 1, -1) != 0);
+ assert(e > 0);
+ assert(strcmp(m, "memset_s : n > RSIZE_MAX") == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, null_ptr);
+ ATF_TP_ADD_TC(tp, smax_gt_rmax);
+ ATF_TP_ADD_TC(tp, smax_lt_zero);
+ ATF_TP_ADD_TC(tp, normal);
+ ATF_TP_ADD_TC(tp, n_gt_rmax);
+ ATF_TP_ADD_TC(tp, n_lt_zero);
+ ATF_TP_ADD_TC(tp, n_gt_smax);
+ ATF_TP_ADD_TC(tp, n_lt_smax);
+ ATF_TP_ADD_TC(tp, smax_gt_rmax_handler);
+ ATF_TP_ADD_TC(tp, smax_lt_zero_handler);
+ ATF_TP_ADD_TC(tp, n_gt_rmax_handler);
+ ATF_TP_ADD_TC(tp, n_lt_zero_handler);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/string/memset_test.c b/lib/libc/tests/string/memset_test.c
new file mode 100644
index 000000000000..b898ad5af251
--- /dev/null
+++ b/lib/libc/tests/string/memset_test.c
@@ -0,0 +1,29 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Strahinja Stanisic <strajabot@FreeBSD.org>
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(int_char_conv);
+ATF_TC_BODY(int_char_conv, tc)
+{
+ char b[64];
+ int c = 0xDEADBEEF;
+ memset(&b, c, 64);
+ for(int i = 0; i < 64; i++) {
+ assert(b[i] == (char)c);
+ }
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, int_char_conv);
+ return (atf_no_error());
+}
+
diff --git a/lib/libc/tests/string/stpncpy_test.c b/lib/libc/tests/string/stpncpy_test.c
new file mode 100644
index 000000000000..8574b2d591be
--- /dev/null
+++ b/lib/libc/tests/string/stpncpy_test.c
@@ -0,0 +1,181 @@
+/*-
+ * Copyright (c) 2009 David Schultz <das@FreeBSD.org>
+ * Copyright (c) 2023 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Robert Clausecker
+ * <fuz@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+static char *(*stpncpy_fn)(char *restrict, const char *restrict, size_t);
+
+static char *
+makebuf(size_t len, int guard_at_end)
+{
+ char *buf;
+ size_t alloc_size, page_size;
+
+ page_size = getpagesize();
+ alloc_size = roundup2(len, page_size) + page_size;
+
+ buf = mmap(NULL, alloc_size, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
+ assert(buf);
+ if (guard_at_end) {
+ assert(munmap(buf + alloc_size - page_size, page_size) == 0);
+ return (buf + alloc_size - page_size - len);
+ } else {
+ assert(munmap(buf, page_size) == 0);
+ return (buf + page_size);
+ }
+}
+
+static void
+test_stpncpy(const char *s)
+{
+ char *src, *dst;
+ size_t size, len, bufsize, x;
+ int i, j;
+
+ size = strlen(s) + 1;
+ for (i = 0; i <= 1; i++) {
+ for (j = 0; j <= 1; j++) {
+ for (bufsize = 0; bufsize <= size + 10; bufsize++) {
+ src = makebuf(size, i);
+ memcpy(src, s, size);
+ dst = makebuf(bufsize, j);
+ memset(dst, 'X', bufsize);
+ len = (bufsize < size) ? bufsize : size - 1;
+ assert(stpncpy_fn(dst, src, bufsize) == dst+len);
+ assert(memcmp(src, dst, len) == 0);
+ for (x = len; x < bufsize; x++)
+ assert(dst[x] == '\0');
+ }
+ }
+ }
+}
+
+static void
+test_sentinel(char *dest, char *src, size_t destlen, size_t srclen)
+{
+ size_t i;
+ const char *res, *wantres;
+ const char *fail = NULL;
+
+ for (i = 0; i < srclen; i++)
+ /* src will never include (){} */
+ src[i] = '0' + i;
+ src[srclen] = '\0';
+
+ /* source sentinels: not to be copied */
+ src[-1] = '(';
+ src[srclen+1] = ')';
+
+ memset(dest, 0xee, destlen);
+
+ /* destination sentinels: not to be touched */
+ dest[-1] = '{';
+ dest[destlen] = '}';
+
+ wantres = dest + (srclen > destlen ? destlen : srclen);
+ res = stpncpy_fn(dest, src, destlen);
+
+ if (dest[-1] != '{')
+ fail = "start sentinel overwritten";
+ else if (dest[destlen] != '}')
+ fail = "end sentinel overwritten";
+ else if (strncmp(src, dest, destlen) != 0)
+ fail = "string not copied correctly";
+ else if (res != wantres)
+ fail = "incorrect return value";
+ else for (i = srclen; i < destlen; i++)
+ if (dest[i] != '\0') {
+ fail = "incomplete NUL padding";
+ break;
+ }
+
+ if (fail)
+ atf_tc_fail_nonfatal("%s\n"
+ "stpncpy(%p \"%s\", %p \"%s\", %zu) = %p (want %p)\n",
+ fail, dest, dest, src, src, destlen, res, wantres);
+}
+
+ATF_TC_WITHOUT_HEAD(null);
+ATF_TC_BODY(null, tc)
+{
+ ATF_CHECK_EQ(stpncpy_fn(NULL, NULL, 0), NULL);
+}
+
+ATF_TC_WITHOUT_HEAD(bounds);
+ATF_TC_BODY(bounds, tc)
+{
+ size_t i;
+ char buf[64+1];
+
+ for (i = 0; i < sizeof(buf) - 1; i++) {
+ buf[i] = ' ' + i;
+ buf[i+1] = '\0';
+ test_stpncpy(buf);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(alignments);
+ATF_TC_BODY(alignments, tc)
+{
+ size_t srcalign, destalign, srclen, destlen;
+ char src[15+3+64]; /* 15 offsets + 64 max length + NUL + sentinels */
+ char dest[15+2+64]; /* 15 offsets + 64 max length + sentinels */
+
+ for (srcalign = 0; srcalign < 16; srcalign++)
+ for (destalign = 0; destalign < 16; destalign++)
+ for (srclen = 0; srclen < 64; srclen++)
+ for (destlen = 0; destlen < 64; destlen++)
+ test_sentinel(dest+destalign+1,
+ src+srcalign+1, destlen, srclen);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ void *dl_handle;
+
+ dl_handle = dlopen(NULL, RTLD_LAZY);
+ stpncpy_fn = dlsym(dl_handle, "test_stpncpy");
+ if (stpncpy_fn == NULL)
+ stpncpy_fn = stpncpy;
+
+ ATF_TP_ADD_TC(tp, null);
+ ATF_TP_ADD_TC(tp, bounds);
+ ATF_TP_ADD_TC(tp, alignments);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/string/strcmp_test.c b/lib/libc/tests/string/strcmp_test.c
new file mode 100644
index 000000000000..75ebcbcadda2
--- /dev/null
+++ b/lib/libc/tests/string/strcmp_test.c
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Robert Clausecker <fuz@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE
+ */
+
+#include <sys/cdefs.h>
+
+#include <atf-c.h>
+#include <dlfcn.h>
+#include <string.h>
+
+int (*volatile strcmp_fn)(const char *, const char *);
+
+ATF_TC(strcmp_alignments);
+ATF_TC_HEAD(strcmp_alignments, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test strcmp(3) with various alignments");
+}
+
+static void
+alignment_testcase(char *a, char *b, int want)
+{
+ int res;
+
+ res = strcmp_fn(a, b);
+ ATF_CHECK_MSG(want == (res > 0) - (res < 0),
+ "strcmp(%p \"%s\", %p \"%s\") = %d != %d",
+ (void *)a, a, (void *)b, b, res, want);
+}
+
+static void
+check_strcmp_alignments(char a[], char b[],
+ size_t a_off, size_t b_off, size_t len, size_t pos)
+{
+ char *a_str, *b_str, a_orig, b_orig;
+
+ a[a_off] = '\0';
+ b[b_off] = '\0';
+
+ a_str = a + a_off + 1;
+ b_str = b + b_off + 1;
+
+ a_str[len] = '\0';
+ b_str[len] = '\0';
+ a_str[len+1] = 'A';
+ b_str[len+1] = 'B';
+
+ a_orig = a_str[pos];
+ b_orig = b_str[pos];
+
+ alignment_testcase(a_str, b_str, 0);
+
+ if (pos < len) {
+ a_str[pos] = '\0';
+ alignment_testcase(a_str, b_str, -1);
+ a_str[pos] = a_orig;
+ b_str[pos] = '\0';
+ alignment_testcase(a_str, b_str, 1);
+ b_str[pos] = b_orig;
+ }
+
+ a_str[pos] = 'X';
+ alignment_testcase(a_str, b_str, 1);
+ a_str[pos] = a_orig;
+ b_str[pos] = 'X';
+ alignment_testcase(a_str, b_str, -1);
+ b_str[pos] = b_orig;
+
+ a[a_off] = '-';
+ b[b_off] = '-';
+ a_str[len] = '-';
+ b_str[len] = '-';
+ a_str[len+1] = '-';
+ b_str[len+1] = '-';
+}
+
+ATF_TC_BODY(strcmp_alignments, tc)
+{
+ size_t a_off, b_off, len, pos;
+ /* 16B alignment offset + 64B buffer + sentinel before/after + NUL */
+ char a[64+16+3], b[64+16+3];
+
+ memset(a, '-', sizeof(a));
+ memset(b, '-', sizeof(b));
+ a[sizeof(a) - 1] = '\0';
+ b[sizeof(b) - 1] = '\0';
+
+ /* check alignment offsets relevant for SSE routines */
+ for (a_off = 0; a_off < 16; a_off++)
+ for (b_off = 0; b_off < 16; b_off++)
+ /* ensure main loop (@ 32B) is completed at least once */
+ for (len = 1; len <= 64; len++)
+ for (pos = 0; pos <= len; pos++)
+ check_strcmp_alignments(a, b, a_off, b_off, len, pos);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ void *dl_handle;
+
+ dl_handle = dlopen(NULL, RTLD_LAZY);
+ strcmp_fn = dlsym(dl_handle, "test_strcmp");
+ if (strcmp_fn == NULL)
+ strcmp_fn = strcmp;
+
+ ATF_TP_ADD_TC(tp, strcmp_alignments);
+
+ return atf_no_error();
+}
diff --git a/lib/libc/tests/string/strcspn_test.c b/lib/libc/tests/string/strcspn_test.c
new file mode 100644
index 000000000000..293fc2bc8d4e
--- /dev/null
+++ b/lib/libc/tests/string/strcspn_test.c
@@ -0,0 +1,269 @@
+/*-
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Robert Clausecker <fuz@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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 <atf-c.h>
+#include <assert.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+
+enum {
+ MAXALIGN = 16, /* test all offsets from this alignment */
+ MAXBUF = 64, /* test up to this buffer length */
+};
+
+enum { NOMATCH, MATCH };
+
+#ifdef STRSPN
+#define STRXSPN strspn
+#else
+#define STRXSPN strcspn
+#endif
+
+static void
+testcase(char *buf, size_t buflen, char *set, size_t setlen, int want_match)
+{
+ size_t i, outcome, expected;
+
+ assert(setlen < UCHAR_MAX - 2);
+
+ for (i = 0; i < buflen; i++)
+#ifdef STRSPN
+ buf[i] = UCHAR_MAX - i % (setlen > 0 ? setlen : 1);
+#else /* strcspn */
+ buf[i] = 1 + i % (UCHAR_MAX - setlen - 1);
+#endif
+
+ buf[i] = '\0';
+
+ for (i = 0; i < setlen; i++)
+ set[i] = UCHAR_MAX - i;
+
+ set[i] = '\0';
+
+#ifdef STRSPN
+ if (setlen == 0)
+ expected = 0;
+ else if (want_match == MATCH && buflen > 0) {
+ buf[buflen - 1] = 1;
+ expected = buflen - 1;
+ } else
+ expected = buflen;
+#else /* strcspn */
+ if (want_match == MATCH && buflen > 0 && setlen > 0) {
+ buf[buflen - 1] = UCHAR_MAX;
+ expected = buflen - 1;
+ } else
+ expected = buflen;
+#endif
+
+ outcome = STRXSPN(buf, set);
+ ATF_CHECK_EQ_MSG(expected, outcome, "%s(%p[%zu], %p[%zu]) = %zu != %zu",
+ __XSTRING(STRXSPN), buf, buflen, set, setlen, outcome, expected);
+}
+
+/* test set with all alignments and lengths of buf */
+static void
+test_buf_alignments(char *set, size_t setlen, int want_match)
+{
+ char buf[MAXALIGN + MAXBUF + 1];
+ size_t i, j;
+
+ for (i = 0; i < MAXALIGN; i++)
+ for (j = 0; j <= MAXBUF; j++)
+ testcase(buf + i, j, set, setlen, want_match);
+}
+
+/* test buf with all alignments and lengths of set */
+static void
+test_set_alignments(char *buf, size_t buflen, int want_match)
+{
+ char set[MAXALIGN + MAXBUF + 1];
+ size_t i, j;
+
+ for (i = 0; i < MAXALIGN; i++)
+ for (j = 0; j <= MAXBUF; j++)
+ testcase(buf, buflen, set + i, j, want_match);
+}
+
+ATF_TC_WITHOUT_HEAD(buf_alignments);
+ATF_TC_BODY(buf_alignments, tc)
+{
+ char set[41];
+
+ test_buf_alignments(set, 0, MATCH);
+ test_buf_alignments(set, 1, MATCH);
+ test_buf_alignments(set, 5, MATCH);
+ test_buf_alignments(set, 20, MATCH);
+ test_buf_alignments(set, 40, MATCH);
+
+ test_buf_alignments(set, 0, NOMATCH);
+ test_buf_alignments(set, 1, NOMATCH);
+ test_buf_alignments(set, 5, NOMATCH);
+ test_buf_alignments(set, 20, NOMATCH);
+ test_buf_alignments(set, 40, NOMATCH);
+}
+
+ATF_TC_WITHOUT_HEAD(set_alignments);
+ATF_TC_BODY(set_alignments, tc)
+{
+ char buf[31];
+
+ test_set_alignments(buf, 0, MATCH);
+ test_set_alignments(buf, 10, MATCH);
+ test_set_alignments(buf, 20, MATCH);
+ test_set_alignments(buf, 30, MATCH);
+
+ test_set_alignments(buf, 0, NOMATCH);
+ test_set_alignments(buf, 10, NOMATCH);
+ test_set_alignments(buf, 20, NOMATCH);
+ test_set_alignments(buf, 30, NOMATCH);
+}
+
+#ifndef STRSPN
+/* test all positions in which set could match buf */
+static void
+test_match_positions(char *buf, char *set, size_t buflen, size_t setlen)
+{
+ size_t i, j, outcome;
+
+ memset(buf, '-', buflen);
+
+ for (i = 0; i < setlen; i++)
+ set[i] = 'A' + i;
+
+ buf[buflen] = '\0';
+ set[setlen] = '\0';
+
+ /*
+ * Check for (mis)match at buffer position i
+ * against set position j.
+ */
+ for (i = 0; i < buflen; i++) {
+ for (j = 0; j < setlen; j++) {
+ buf[i] = set[j];
+
+ outcome = strcspn(buf, set);
+ ATF_CHECK_EQ_MSG(i, outcome,
+ "strcspn(\"%s\", \"%s\") = %zu != %zu",
+ buf, set, outcome, i);
+ }
+
+ buf[i] = '-';
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(match_positions);
+ATF_TC_BODY(match_positions, tc)
+{
+ char buf[129], set[65];
+
+ test_match_positions(buf, set, 128, 64);
+ test_match_positions(buf, set, 64, 64);
+ test_match_positions(buf, set, 32, 64);
+ test_match_positions(buf, set, 16, 64);
+ test_match_positions(buf, set, 8, 64);
+ test_match_positions(buf, set, 128, 32);
+ test_match_positions(buf, set, 64, 32);
+ test_match_positions(buf, set, 32, 32);
+ test_match_positions(buf, set, 16, 32);
+ test_match_positions(buf, set, 8, 32);
+ test_match_positions(buf, set, 128, 16);
+ test_match_positions(buf, set, 64, 16);
+ test_match_positions(buf, set, 32, 16);
+ test_match_positions(buf, set, 16, 16);
+ test_match_positions(buf, set, 8, 16);
+ test_match_positions(buf, set, 128, 8);
+ test_match_positions(buf, set, 64, 8);
+ test_match_positions(buf, set, 32, 8);
+ test_match_positions(buf, set, 16, 8);
+ test_match_positions(buf, set, 8, 8);
+}
+
+/* if there are two matches, check that the earlier match is taken */
+static void
+test_match_order(char *buf, char *set, size_t buflen, size_t setlen)
+{
+ size_t i, j, k, l, outcome;
+
+ memset(buf, '-', buflen);
+
+ for (i = 0; i < setlen; i++)
+ set[i] = 'A' + i;
+
+ buf[buflen] = '\0';
+ set[setlen] = '\0';
+
+ for (i = 0; i < setlen; i++)
+ for (j = 0; j < setlen; j++)
+ for (k = 0; k + 1 < buflen; k++)
+ for (l = k + 1; l < buflen; l++) {
+ buf[k] = set[i];
+ buf[l] = set[j];
+ outcome = strcspn(buf, set);
+ ATF_CHECK_EQ_MSG(k, outcome,
+ "strcspn(\"%s\", \"%s\") = %zu != %zu",
+ buf, set, outcome, k);
+ buf[k] = '-';
+ buf[l] = '-';
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(match_order);
+ATF_TC_BODY(match_order, tc)
+{
+ char buf[33], set[65];
+
+ test_match_order(buf, set, 32, 64);
+ test_match_order(buf, set, 16, 64);
+ test_match_order(buf, set, 8, 64);
+ test_match_order(buf, set, 32, 32);
+ test_match_order(buf, set, 16, 32);
+ test_match_order(buf, set, 8, 32);
+ test_match_order(buf, set, 32, 16);
+ test_match_order(buf, set, 16, 16);
+ test_match_order(buf, set, 8, 16);
+ test_match_order(buf, set, 32, 8);
+ test_match_order(buf, set, 16, 8);
+ test_match_order(buf, set, 8, 8);
+}
+#endif /* !defined(STRSPN) */
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, buf_alignments);
+ ATF_TP_ADD_TC(tp, set_alignments);
+#ifndef STRSPN
+ ATF_TP_ADD_TC(tp, match_positions);
+ ATF_TP_ADD_TC(tp, match_order);
+#endif
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/string/strerror_test.c b/lib/libc/tests/string/strerror_test.c
new file mode 100644
index 000000000000..8f6cbfe38a59
--- /dev/null
+++ b/lib/libc/tests/string/strerror_test.c
@@ -0,0 +1,204 @@
+/*-
+ * Copyright (c) 2001 Wes Peters <wes@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+static char buf[64];
+static char *sret;
+static int iret;
+
+ATF_TC_WITHOUT_HEAD(strerror_unknown_error);
+ATF_TC_BODY(strerror_unknown_error, tc)
+{
+
+ errno = 0;
+ sret = strerror(INT_MAX);
+ snprintf(buf, sizeof(buf), "Unknown error: %d", INT_MAX);
+ ATF_CHECK(strcmp(sret, buf) == 0);
+ ATF_CHECK(errno == EINVAL);
+}
+
+ATF_TC_WITHOUT_HEAD(strerror_no_error);
+ATF_TC_BODY(strerror_no_error, tc)
+{
+
+ errno = 0;
+ sret = strerror(0);
+ ATF_CHECK(strcmp(sret, "No error: 0") == 0);
+ ATF_CHECK(errno == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(strerror_EPERM_test);
+ATF_TC_BODY(strerror_EPERM_test, tc)
+{
+
+ errno = 0;
+ sret = strerror(EPERM);
+ ATF_CHECK(strcmp(sret, "Operation not permitted") == 0);
+ ATF_CHECK(errno == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(strerror_EPFNOSUPPORT_test);
+ATF_TC_BODY(strerror_EPFNOSUPPORT_test, tc)
+{
+
+ errno = 0;
+ sret = strerror(EPFNOSUPPORT);
+ ATF_CHECK(strcmp(sret, "Protocol family not supported") == 0);
+ ATF_CHECK(errno == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(strerror_ELAST_test);
+ATF_TC_BODY(strerror_ELAST_test, tc)
+{
+
+ errno = 0;
+ sret = strerror(ELAST);
+ ATF_CHECK(errno == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(strerror_r__unknown_error);
+ATF_TC_BODY(strerror_r__unknown_error, tc)
+{
+
+ memset(buf, '*', sizeof(buf));
+ iret = strerror_r(-1, buf, sizeof(buf));
+ ATF_CHECK(strcmp(buf, "Unknown error: -1") == 0);
+ ATF_CHECK(iret == EINVAL);
+}
+
+ATF_TC_WITHOUT_HEAD(strerror_r__EPERM_one_byte_short);
+ATF_TC_BODY(strerror_r__EPERM_one_byte_short, tc)
+{
+
+ memset(buf, '*', sizeof(buf));
+ /* One byte too short. */
+ iret = strerror_r(EPERM, buf, strlen("Operation not permitted"));
+ ATF_CHECK(strcmp(buf, "Operation not permitte") == 0);
+ ATF_CHECK(iret == ERANGE);
+}
+
+ATF_TC_WITHOUT_HEAD(strerror_r__EPERM_unknown_error_one_byte_short);
+ATF_TC_BODY(strerror_r__EPERM_unknown_error_one_byte_short, tc)
+{
+
+ memset(buf, '*', sizeof(buf));
+ /* One byte too short. */
+ iret = strerror_r(-1, buf, strlen("Unknown error: -1"));
+ ATF_CHECK(strcmp(buf, "Unknown error: -") == 0);
+ ATF_CHECK(iret == EINVAL);
+}
+
+ATF_TC_WITHOUT_HEAD(strerror_r__EPERM_unknown_error_two_bytes_short);
+ATF_TC_BODY(strerror_r__EPERM_unknown_error_two_bytes_short, tc)
+{
+
+ memset(buf, '*', sizeof(buf));
+ /* Two bytes too short. */
+ iret = strerror_r(-2, buf, strlen("Unknown error: -2") - 1);
+ ATF_CHECK(strcmp(buf, "Unknown error: ") == 0);
+ ATF_CHECK(iret == EINVAL);
+}
+
+ATF_TC_WITHOUT_HEAD(strerror_r__EPERM_unknown_error_three_bytes_short);
+ATF_TC_BODY(strerror_r__EPERM_unknown_error_three_bytes_short, tc)
+{
+
+ memset(buf, '*', sizeof(buf));
+ /* Three bytes too short. */
+ iret = strerror_r(-2, buf, strlen("Unknown error: -2") - 2);
+ ATF_CHECK(strcmp(buf, "Unknown error:") == 0);
+ ATF_CHECK(iret == EINVAL);
+}
+
+ATF_TC_WITHOUT_HEAD(strerror_r__EPERM_unknown_error_12345_one_byte_short);
+ATF_TC_BODY(strerror_r__EPERM_unknown_error_12345_one_byte_short, tc)
+{
+
+ memset(buf, '*', sizeof(buf));
+ /* One byte too short. */
+ iret = strerror_r(12345, buf, strlen("Unknown error: 12345"));
+ ATF_CHECK(strcmp(buf, "Unknown error: 1234") == 0);
+ ATF_CHECK(iret == EINVAL);
+}
+
+ATF_TC_WITHOUT_HEAD(strerror_r__no_error);
+ATF_TC_BODY(strerror_r__no_error, tc)
+{
+
+ memset(buf, '*', sizeof(buf));
+ iret = strerror_r(0, buf, sizeof(buf));
+ ATF_CHECK(strcmp(buf, "No error: 0") == 0);
+ ATF_CHECK(iret == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(strerror_r__EDEADLK);
+ATF_TC_BODY(strerror_r__EDEADLK, tc)
+{
+
+ memset(buf, '*', sizeof(buf));
+ iret = strerror_r(EDEADLK, buf, sizeof(buf));
+ ATF_CHECK(strcmp(buf, "Resource deadlock avoided") == 0);
+ ATF_CHECK(iret == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(strerror_r__EPROCLIM);
+ATF_TC_BODY(strerror_r__EPROCLIM, tc)
+{
+
+ memset(buf, '*', sizeof(buf));
+ iret = strerror_r(EPROCLIM, buf, sizeof(buf));
+ ATF_CHECK(strcmp(buf, "Too many processes") == 0);
+ ATF_CHECK(iret == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, strerror_unknown_error);
+ ATF_TP_ADD_TC(tp, strerror_no_error);
+ ATF_TP_ADD_TC(tp, strerror_EPERM_test);
+ ATF_TP_ADD_TC(tp, strerror_EPFNOSUPPORT_test);
+ ATF_TP_ADD_TC(tp, strerror_ELAST_test);
+ ATF_TP_ADD_TC(tp, strerror_r__unknown_error);
+ ATF_TP_ADD_TC(tp, strerror_r__EPERM_one_byte_short);
+ ATF_TP_ADD_TC(tp, strerror_r__EPERM_unknown_error_one_byte_short);
+ ATF_TP_ADD_TC(tp, strerror_r__EPERM_unknown_error_two_bytes_short);
+ ATF_TP_ADD_TC(tp, strerror_r__EPERM_unknown_error_three_bytes_short);
+ ATF_TP_ADD_TC(tp, strerror_r__EPERM_unknown_error_12345_one_byte_short);
+ ATF_TP_ADD_TC(tp, strerror_r__no_error);
+ ATF_TP_ADD_TC(tp, strerror_r__EDEADLK);
+ ATF_TP_ADD_TC(tp, strerror_r__EPROCLIM);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/string/strlcpy_test.c b/lib/libc/tests/string/strlcpy_test.c
new file mode 100644
index 000000000000..646bef42683e
--- /dev/null
+++ b/lib/libc/tests/string/strlcpy_test.c
@@ -0,0 +1,183 @@
+/*-
+ * Copyright (c) 2009 David Schultz <das@FreeBSD.org>
+ * Copyright (c) 2023 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by Robert Clausecker
+ * <fuz@FreeBSD.org> under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+size_t (*strlcpy_fn)(char *restrict, const char *restrict, size_t);
+
+static char *
+makebuf(size_t len, int guard_at_end)
+{
+ char *buf;
+ size_t alloc_size, page_size;
+
+ page_size = getpagesize();
+ alloc_size = roundup2(len, page_size) + page_size;
+
+ buf = mmap(NULL, alloc_size, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
+ assert(buf);
+ if (guard_at_end) {
+ assert(munmap(buf + alloc_size - page_size, page_size) == 0);
+ return (buf + alloc_size - page_size - len);
+ } else {
+ assert(munmap(buf, page_size) == 0);
+ return (buf + page_size);
+ }
+}
+
+static void
+test_strlcpy(const char *s)
+{
+ char *src, *dst;
+ size_t size, bufsize, x;
+ int i, j;
+
+ size = strlen(s) + 1;
+ for (i = 0; i <= 1; i++) {
+ for (j = 0; j <= 1; j++) {
+ for (bufsize = 0; bufsize <= size + 10; bufsize++) {
+ src = makebuf(size, i);
+ memcpy(src, s, size);
+ dst = makebuf(bufsize, j);
+ memset(dst, 'X', bufsize);
+ assert(strlcpy_fn(dst, src, bufsize) == size-1);
+ assert(bufsize == 0 || strncmp(src, dst, bufsize - 1) == 0);
+ for (x = size; x < bufsize; x++)
+ assert(dst[x] == 'X');
+ }
+ }
+ }
+}
+
+static void
+test_sentinel(char *dest, char *src, size_t destlen, size_t srclen)
+{
+ size_t i;
+ size_t res, wantres;
+ const char *fail = NULL;
+
+ for (i = 0; i < srclen; i++)
+ /* src will never include (){} */
+ src[i] = '0' + i;
+ src[srclen] = '\0';
+
+ /* source sentinels: not to be copied */
+ src[-1] = '(';
+ src[srclen+1] = ')';
+
+ memset(dest, '\xee', destlen);
+
+ /* destination sentinels: not to be touched */
+ dest[-1] = '{';
+ dest[destlen] = '}';
+
+ wantres = srclen;
+ res = strlcpy_fn(dest, src, destlen);
+
+ if (dest[-1] != '{')
+ fail = "start sentinel overwritten";
+ else if (dest[destlen] != '}')
+ fail = "end sentinel overwritten";
+ else if (res != wantres)
+ fail = "incorrect return value";
+ else if (destlen > 0 && strncmp(src, dest, destlen - 1) != 0)
+ fail = "string not copied correctly";
+ else if (destlen > 0 && srclen >= destlen - 1 && dest[destlen-1] != '\0')
+ fail = "string not NUL terminated";
+ else for (i = srclen + 1; i < destlen; i++)
+ if (dest[i] != '\xee') {
+ fail = "buffer mutilated behind string";
+ break;
+ }
+
+ if (fail)
+ atf_tc_fail_nonfatal("%s\n"
+ "strlcpy(%p \"%s\", %p \"%s\", %zu) = %zu (want %zu)\n",
+ fail, dest, dest, src, src, destlen, res, wantres);
+}
+
+ATF_TC_WITHOUT_HEAD(null);
+ATF_TC_BODY(null, tc)
+{
+ ATF_CHECK_EQ(strlcpy_fn(NULL, "foo", 0), 3);
+}
+
+ATF_TC_WITHOUT_HEAD(bounds);
+ATF_TC_BODY(bounds, tc)
+{
+ size_t i;
+ char buf[64+1];
+
+ for (i = 0; i < sizeof(buf) - 1; i++) {
+ buf[i] = ' ' + i;
+ buf[i+1] = '\0';
+ test_strlcpy(buf);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(alignments);
+ATF_TC_BODY(alignments, tc)
+{
+ size_t srcalign, destalign, srclen, destlen;
+ char src[15+3+64]; /* 15 offsets + 64 max length + NUL + sentinels */
+ char dest[15+2+64]; /* 15 offsets + 64 max length + sentinels */
+
+ for (srcalign = 0; srcalign < 16; srcalign++)
+ for (destalign = 0; destalign < 16; destalign++)
+ for (srclen = 0; srclen < 64; srclen++)
+ for (destlen = 0; destlen < 64; destlen++)
+ test_sentinel(dest+destalign+1,
+ src+srcalign+1, destlen, srclen);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ void *dl_handle;
+
+ dl_handle = dlopen(NULL, RTLD_LAZY);
+ strlcpy_fn = dlsym(dl_handle, "test_strlcpy");
+ if (strlcpy_fn == NULL)
+ strlcpy_fn = strlcpy;
+
+ ATF_TP_ADD_TC(tp, null);
+ ATF_TP_ADD_TC(tp, bounds);
+ ATF_TP_ADD_TC(tp, alignments);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/string/strncmp_test.c b/lib/libc/tests/string/strncmp_test.c
new file mode 100644
index 000000000000..989c58bcfedf
--- /dev/null
+++ b/lib/libc/tests/string/strncmp_test.c
@@ -0,0 +1,165 @@
+/*-
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Robert Clausecker <fuz@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE
+ */
+
+#include <sys/cdefs.h>
+
+#include <atf-c.h>
+#include <dlfcn.h>
+#include <string.h>
+
+int (*volatile strncmp_fn)(const char *, const char *, size_t);
+
+static void
+alignment_testcase(char *a, char *b, int want, size_t len)
+{
+ int res;
+
+ res = strncmp_fn(a, b, len);
+ ATF_CHECK_MSG(want == (res > 0) - (res < 0),
+ "strcmp(%p \"%s\", %p \"%s\", %zu) = %d != %d",
+ (void *)a, a, (void *)b, b, len, res, want);
+}
+
+static void
+check_strncmp_alignments(char a[], char b[],
+ size_t a_off, size_t b_off, size_t len, size_t pos)
+{
+ char *a_str, *b_str, a_orig, b_orig;
+
+ a[a_off] = '\0';
+ b[b_off] = '\0';
+
+ a_str = a + a_off + 1;
+ b_str = b + b_off + 1;
+
+ a_str[len] = '\0';
+ b_str[len] = '\0';
+ a_str[len+1] = 'A';
+ b_str[len+1] = 'B';
+
+ a_orig = a_str[pos];
+ b_orig = b_str[pos];
+
+ alignment_testcase(a_str, b_str, 0, len + 16);
+ alignment_testcase(a_str, b_str, 0, len + 1);
+ alignment_testcase(a_str, b_str, 0, len);
+
+ if (pos < len) {
+ a_str[pos] = '\0';
+ alignment_testcase(a_str, b_str, -1, len + 16);
+ alignment_testcase(a_str, b_str, -1, len + 1);
+ alignment_testcase(a_str, b_str, -1, len);
+ alignment_testcase(a_str, b_str, -1, pos + 1);
+ alignment_testcase(a_str, b_str, 0, pos);
+ a_str[pos] = a_orig;
+
+ b_str[pos] = '\0';
+ alignment_testcase(a_str, b_str, 1, len + 16);
+ alignment_testcase(a_str, b_str, 1, len + 1);
+ alignment_testcase(a_str, b_str, 1, len);
+ alignment_testcase(a_str, b_str, 1, pos + 1);
+ alignment_testcase(a_str, b_str, 0, pos);
+ b_str[pos] = b_orig;
+ }
+
+ a_str[pos] = 'X';
+ alignment_testcase(a_str, b_str, 1, len + 16);
+ alignment_testcase(a_str, b_str, 0, pos);
+ alignment_testcase(a_str, b_str, 1, pos + 1);
+ if (pos < len) {
+ alignment_testcase(a_str, b_str, 1, len);
+ alignment_testcase(a_str, b_str, 1, len + 1);
+ }
+ a_str[pos] = a_orig;
+
+ b_str[pos] = 'X';
+ alignment_testcase(a_str, b_str, -1, len + 16);
+ alignment_testcase(a_str, b_str, 0, pos);
+ alignment_testcase(a_str, b_str, -1, pos + 1);
+ if (pos < len) {
+ alignment_testcase(a_str, b_str, -1, len);
+ alignment_testcase(a_str, b_str, -1, len + 1);
+ }
+ b_str[pos] = b_orig;
+
+ a[a_off] = '-';
+ b[b_off] = '-';
+ a_str[len] = '-';
+ b_str[len] = '-';
+ a_str[len+1] = '-';
+ b_str[len+1] = '-';
+}
+
+ATF_TC(strncmp_alignments);
+ATF_TC_HEAD(strncmp_alignments, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test strncmp(3) with various alignments");
+}
+
+ATF_TC_BODY(strncmp_alignments, tc)
+{
+ size_t a_off, b_off, len, pos;
+ char a[64+16+16+3], b[64+16+16+3];
+
+ memset(a, '-', sizeof(a));
+ memset(b, '-', sizeof(b));
+ a[sizeof(a) - 1] = '\0';
+ b[sizeof(b) - 1] = '\0';
+
+ for (a_off = 0; a_off < 16; a_off++)
+ for (b_off = 0; b_off < 16; b_off++)
+ for (len = 1; len <= 64; len++)
+ for (pos = 0; pos <= len; pos++)
+ check_strncmp_alignments(a, b, a_off, b_off, len, pos);
+}
+
+ATF_TC(strncmp_null);
+ATF_TC_HEAD(strncmp_null, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test strncmp(3) with null pointers");
+}
+
+ATF_TC_BODY(strncmp_null, tc)
+{
+ alignment_testcase(NULL, NULL, 0, 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ void *dl_handle;
+
+ dl_handle = dlopen(NULL, RTLD_LAZY);
+ strncmp_fn = dlsym(dl_handle, "test_strncmp");
+ if (strncmp_fn == NULL)
+ strncmp_fn = strncmp;
+
+ ATF_TP_ADD_TC(tp, strncmp_alignments);
+ ATF_TP_ADD_TC(tp, strncmp_null);
+
+ return atf_no_error();
+}
diff --git a/lib/libc/tests/string/strnlen_test.c b/lib/libc/tests/string/strnlen_test.c
new file mode 100644
index 000000000000..31c2384bb30f
--- /dev/null
+++ b/lib/libc/tests/string/strnlen_test.c
@@ -0,0 +1,141 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Strahinja Stanisic <strajabot@FreeBSD.org>
+ */
+
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdalign.h>
+#include <stdint.h>
+
+#include <atf-c.h>
+
+ATF_TC(strnlen_alignments);
+ATF_TC_HEAD(strnlen_alignments, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test strnlen(3) with different alignments");
+}
+
+ATF_TC_BODY(strnlen_alignments, tc)
+{
+ size_t (*strnlen_fn)(const char*, size_t) = strnlen;
+ char alignas(16) buffer[1 + 16 + 64 + 1 + 1];
+
+ memset(buffer, '/', sizeof(buffer));
+
+ for (int align = 1; align < 1 + 16; align++) {
+ char *s = buffer + align;
+
+ for (size_t maxlen = 0; maxlen <= 64; maxlen++) {
+ for (size_t len = 0; len <= maxlen; len++) {
+ /* returns length */
+
+ /* without sentinels */
+ s[len] = '\0';
+ size_t val = strnlen_fn(s, maxlen);
+ if (val != len) {
+ fprintf(stderr, "align = %d, maxlen = %zu, len = %zu",
+ align, maxlen, len);
+ atf_tc_fail("returned incorrect len");
+ }
+
+ /* with sentinels */
+ s[-1] = '\0';
+ s[maxlen + 1] = '\0';
+ val = strnlen_fn(s, maxlen);
+ if (val != len) {
+ fprintf(stderr, "align = %d, maxlen = %zu, len = %zu",
+ align, maxlen, len);
+ atf_tc_fail("returned incorrect len (sentinels)");
+ }
+
+ /* cleanup */
+ s[-1] = '/';
+ s[len] = '/';
+ s[maxlen + 1] = '/';
+
+ }
+
+ /* returns maxlen */
+
+ /* without sentinels */
+ size_t val = strnlen_fn(s, maxlen);
+ if (val != maxlen) {
+ fprintf(stderr, "align = %d, maxlen = %zu",
+ align, maxlen);
+ atf_tc_fail("should return maxlen");
+ }
+
+ /* with sentinels */
+ s[-1] = '\0';
+ s[maxlen + 1] = '\0';
+ val = strnlen_fn(s, maxlen);
+ if (val != maxlen) {
+ fprintf(stderr, "align = %d, maxlen = %zu",
+ align, maxlen);
+ atf_tc_fail("should return maxlen (sentinels)");
+ }
+
+ /* cleanup */
+ s[-1] = '/';
+ s[maxlen + 1] = '/';
+ }
+ }
+}
+
+ATF_TC(strnlen_size_max);
+ATF_TC_HEAD(strnlen_size_max, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test strnlen(3) with maxlen=SIZE_MAX");
+}
+
+ATF_TC_BODY(strnlen_size_max, tc)
+{
+ size_t (*strnlen_fn)(const char*, size_t) = strnlen;
+ char alignas(16) buffer[1 + 16 + 64 + 1 + 1];
+
+ memset(buffer, '/', sizeof(buffer));
+
+ for (int align = 1; align < 1 + 16; align++) {
+ char* s = buffer + align;
+
+ for (size_t len = 0; len <= 64; len++) {
+ /* returns length */
+
+ /* without sentinels */
+ s[len] = '\0';
+ size_t val = strnlen_fn(s, SIZE_MAX);
+ if (val != len) {
+ fprintf(stderr, "align = %d, maxlen = %zu, len = %zu",
+ align, SIZE_MAX, len);
+ atf_tc_fail("returned incorrect len (SIZE_MAX)");
+ }
+
+ /* with sentinels */
+ s[-1] = '\0';
+ val = strnlen_fn(s, SIZE_MAX);
+ if (val != len) {
+ fprintf(stderr, "align = %d, maxlen = %zu, len = %zu",
+ align, SIZE_MAX, len);
+ atf_tc_fail("returned incorrect len (sentinels) (SIZE_MAX)");
+ }
+
+ /* cleanup */
+ s[-1] = '/';
+ s[len] = '/';
+ }
+ }
+}
+
+
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, strnlen_alignments);
+ ATF_TP_ADD_TC(tp, strnlen_size_max);
+
+ return atf_no_error();
+}
diff --git a/lib/libc/tests/string/strspn_test.c b/lib/libc/tests/string/strspn_test.c
new file mode 100644
index 000000000000..73a08ddefa1b
--- /dev/null
+++ b/lib/libc/tests/string/strspn_test.c
@@ -0,0 +1,30 @@
+/*-
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Robert Clausecker <fuz@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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
+ */
+
+#define STRSPN
+#include "strcspn_test.c"
diff --git a/lib/libc/tests/string/strverscmp_test.c b/lib/libc/tests/string/strverscmp_test.c
new file mode 100644
index 000000000000..fd6a2620cb48
--- /dev/null
+++ b/lib/libc/tests/string/strverscmp_test.c
@@ -0,0 +1,93 @@
+/*-
+* SPDX-License-Identifier: BSD-2-Clause
+* Copyright (c) 2022 Aymeric Wibo <obiwac@gmail.com>
+*/
+
+#include <atf-c.h>
+#include <string.h>
+
+static void
+check_all(size_t len, const char *ordered[len])
+{
+ const char *a, *b;
+
+ for (size_t i = 0; i < len; i++) {
+ for (size_t j = 0; j < len; j++) {
+ a = ordered[i];
+ b = ordered[j];
+
+ if (i == j)
+ ATF_CHECK_MSG(
+ strverscmp(a, b) == 0,
+ "strverscmp(\"%s\", \"%s\") == 0",
+ a, b
+ );
+ else if (i < j)
+ ATF_CHECK_MSG(
+ strverscmp(a, b) < 0,
+ "strverscmp(\"%s\", \"%s\") < 0",
+ a, b
+ );
+ else if (i > j)
+ ATF_CHECK_MSG(
+ strverscmp(a, b) > 0,
+ "strverscmp(\"%s\", \"%s\") > 0",
+ a, b
+ );
+ }
+ }
+}
+
+#define CHECK_ALL(...) do { \
+ const char *ordered[] = { __VA_ARGS__ }; \
+ check_all(sizeof(ordered) / sizeof(*ordered), ordered); \
+} while (0)
+
+ATF_TC_WITHOUT_HEAD(strcmp_functionality);
+ATF_TC_BODY(strcmp_functionality, tc)
+{
+ CHECK_ALL("", "a", "b");
+}
+
+/* from Linux man page strverscmp(3) */
+
+ATF_TC_WITHOUT_HEAD(vers_ordering);
+ATF_TC_BODY(vers_ordering, tc)
+{
+ CHECK_ALL("000", "00", "01", "010", "09", "0", "1", "9", "10");
+}
+
+ATF_TC_WITHOUT_HEAD(natural_ordering);
+ATF_TC_BODY(natural_ordering, tc)
+{
+ CHECK_ALL("jan1", "jan2", "jan9", "jan10", "jan11", "jan19", "jan20");
+}
+
+/* https://sourceware.org/bugzilla/show_bug.cgi?id=9913 */
+
+ATF_TC_WITHOUT_HEAD(glibc_bug_9913);
+ATF_TC_BODY(glibc_bug_9913, tc)
+{
+ CHECK_ALL(
+ "B0075022800016.gbp.corp.com",
+ "B007502280067.gbp.corp.com",
+ "B007502357019.GBP.CORP.COM"
+ );
+}
+
+ATF_TC_WITHOUT_HEAD(semver_ordering);
+ATF_TC_BODY(semver_ordering, tc)
+{
+ CHECK_ALL("2.6.20", "2.6.21");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, strcmp_functionality);
+ ATF_TP_ADD_TC(tp, vers_ordering);
+ ATF_TP_ADD_TC(tp, natural_ordering);
+ ATF_TP_ADD_TC(tp, glibc_bug_9913);
+ ATF_TP_ADD_TC(tp, semver_ordering);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/string/strxfrm_test.c b/lib/libc/tests/string/strxfrm_test.c
new file mode 100644
index 000000000000..becc620ba79c
--- /dev/null
+++ b/lib/libc/tests/string/strxfrm_test.c
@@ -0,0 +1,48 @@
+/*-
+ * Copyright (c) 2016 Baptiste Daroussin <bapt@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <locale.h>
+#include <stdio.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(iso_8859_5);
+ATF_TC_BODY(iso_8859_5, tc)
+{
+ char s1[8];
+ const char s2[] = { 0xa1, 0 };
+
+ setlocale(LC_ALL, "ru_RU.ISO8859-5");
+ strxfrm(s1, s2, 0x8);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, iso_8859_5);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/string/timingsafe_bcmp_test.c b/lib/libc/tests/string/timingsafe_bcmp_test.c
new file mode 100644
index 000000000000..96bf789633f2
--- /dev/null
+++ b/lib/libc/tests/string/timingsafe_bcmp_test.c
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Robert Clausecker <fuz@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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
+ */
+
+#define MEMCMP timingsafe_bcmp
+#define RES(x) ((x) != 0)
+
+#include "memcmp_test.c"
diff --git a/lib/libc/tests/string/timingsafe_memcmp_test.c b/lib/libc/tests/string/timingsafe_memcmp_test.c
new file mode 100644
index 000000000000..5f97e41fcf8a
--- /dev/null
+++ b/lib/libc/tests/string/timingsafe_memcmp_test.c
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2023 The FreeBSD Foundation
+ *
+ * This software was developed by Robert Clausecker <fuz@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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
+ */
+
+#define MEMCMP timingsafe_memcmp
+#define RES(x) (((x) > 0) - ((x) < 0))
+
+#include "memcmp_test.c"
diff --git a/lib/libc/tests/string/wcscasecmp_test.c b/lib/libc/tests/string/wcscasecmp_test.c
new file mode 100644
index 000000000000..9a47c0d91b31
--- /dev/null
+++ b/lib/libc/tests/string/wcscasecmp_test.c
@@ -0,0 +1,125 @@
+/*-
+ * Copyright (c) 2009 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <wctype.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(nul);
+ATF_TC_BODY(nul, tc)
+{
+
+ ATF_REQUIRE(setlocale(LC_CTYPE, "C") != NULL);
+
+ ATF_CHECK(wcscasecmp(L"", L"") == 0);
+ ATF_CHECK(wcsncasecmp(L"", L"", 50) == 0);
+ ATF_CHECK(wcsncasecmp(L"", L"", 0) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(wcscasecmp_equal);
+ATF_TC_BODY(wcscasecmp_equal, tc)
+{
+
+ ATF_REQUIRE(setlocale(LC_CTYPE, "C") != NULL);
+
+ ATF_CHECK(wcscasecmp(L"abc", L"abc") == 0);
+ ATF_CHECK(wcscasecmp(L"ABC", L"ABC") == 0);
+ ATF_CHECK(wcscasecmp(L"abc", L"ABC") == 0);
+ ATF_CHECK(wcscasecmp(L"ABC", L"abc") == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(wcscasecmp_same_len_buffers);
+ATF_TC_BODY(wcscasecmp_same_len_buffers, tc)
+{
+
+ ATF_REQUIRE(setlocale(LC_CTYPE, "C") != NULL);
+
+ ATF_CHECK(wcscasecmp(L"abc", L"xyz") < 0);
+ ATF_CHECK(wcscasecmp(L"ABC", L"xyz") < 0);
+ ATF_CHECK(wcscasecmp(L"abc", L"XYZ") < 0);
+ ATF_CHECK(wcscasecmp(L"ABC", L"XYZ") < 0);
+ ATF_CHECK(wcscasecmp(L"xyz", L"abc") > 0);
+ ATF_CHECK(wcscasecmp(L"XYZ", L"abc") > 0);
+ ATF_CHECK(wcscasecmp(L"xyz", L"ABC") > 0);
+ ATF_CHECK(wcscasecmp(L"XYZ", L"ABC") > 0);
+}
+
+ATF_TC_WITHOUT_HEAD(wcscasecmp_mismatched_len_buffers);
+ATF_TC_BODY(wcscasecmp_mismatched_len_buffers, tc)
+{
+
+ ATF_REQUIRE(setlocale(LC_CTYPE, "C") != NULL);
+
+ ATF_CHECK(wcscasecmp(L"abc", L"ABCD") < 0);
+ ATF_CHECK(wcscasecmp(L"ABC", L"abcd") < 0);
+ ATF_CHECK(wcscasecmp(L"abcd", L"ABC") > 0);
+ ATF_CHECK(wcscasecmp(L"ABCD", L"abc") > 0);
+}
+
+ATF_TC_WITHOUT_HEAD(wcsncasecmp);
+ATF_TC_BODY(wcsncasecmp, tc)
+{
+
+ ATF_REQUIRE(setlocale(LC_CTYPE, "C") != NULL);
+
+ ATF_CHECK(wcsncasecmp(L"abc", L"ABCD", 4) < 0);
+ ATF_CHECK(wcsncasecmp(L"ABC", L"abcd", 4) < 0);
+ ATF_CHECK(wcsncasecmp(L"abcd", L"ABC", 4) > 0);
+ ATF_CHECK(wcsncasecmp(L"ABCD", L"abc", 4) > 0);
+ ATF_CHECK(wcsncasecmp(L"abc", L"ABCD", 3) == 0);
+ ATF_CHECK(wcsncasecmp(L"ABC", L"abcd", 3) == 0);
+}
+
+ATF_TC_WITHOUT_HEAD(wcscasecmp_greek);
+ATF_TC_BODY(wcscasecmp_greek, tc)
+{
+
+ ATF_REQUIRE(setlocale(LC_CTYPE, "C") != NULL);
+
+ ATF_CHECK(wcscasecmp(L"λ", L"Λ") != 0);
+ ATF_REQUIRE(setlocale(LC_CTYPE, "el_GR.UTF-8") != NULL);
+ ATF_CHECK(wcscasecmp(L"λ", L"Λ") == 0);
+ ATF_CHECK(wcscasecmp(L"λ", L"Ω") < 0);
+ ATF_CHECK(wcscasecmp(L"Ω", L"λ") > 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, nul);
+ ATF_TP_ADD_TC(tp, wcscasecmp_equal);
+ ATF_TP_ADD_TC(tp, wcscasecmp_same_len_buffers);
+ ATF_TP_ADD_TC(tp, wcscasecmp_mismatched_len_buffers);
+ ATF_TP_ADD_TC(tp, wcsncasecmp);
+ ATF_TP_ADD_TC(tp, wcscasecmp_greek);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/string/wcscoll_test.c b/lib/libc/tests/string/wcscoll_test.c
new file mode 100644
index 000000000000..2ce85bbb986b
--- /dev/null
+++ b/lib/libc/tests/string/wcscoll_test.c
@@ -0,0 +1,153 @@
+/*-
+ * Copyright (c) 2016 Baptiste Daroussin <bapt@FreeBSD.org>
+ * Copyright 2016 Tom Lane <tgl@sss.pgh.pa.us>
+ * Copyright 2017 Nexenta Systems, 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 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 <wchar.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <time.h>
+#include <errno.h>
+
+#include <atf-c.h>
+
+static int
+cmp(const void *a, const void *b)
+{
+ const wchar_t wa[2] = { *(const wchar_t *)a, 0 };
+ const wchar_t wb[2] = { *(const wchar_t *)b, 0 };
+
+ return (wcscoll(wa, wb));
+}
+
+ATF_TC_WITHOUT_HEAD(russian_collation);
+ATF_TC_BODY(russian_collation, tc)
+{
+ wchar_t c[] = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzЁАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяё";
+ wchar_t res[] = L"aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZаАбБвВгГдДеЕёЁжЖзЗиИйЙкКлЛмМнНоОпПрРсСтТуУфФхХцЦчЧшШщЩъЪыЫьЬэЭюЮяЯ";
+
+ ATF_CHECK_MSG(setlocale(LC_ALL, "ru_RU.UTF-8") != NULL,
+ "Fail to set locale to \"ru_RU.UTF-8\"");
+ qsort(c, wcslen(c), sizeof(wchar_t), cmp);
+ ATF_CHECK_MSG(wcscmp(c, res) == 0,
+ "Bad collation, expected: '%ls' got '%ls'", res, c);
+}
+
+#define NSTRINGS 2000
+#define MAXSTRLEN 20
+#define MAXXFRMLEN (MAXSTRLEN * 20)
+
+typedef struct {
+ char sval[MAXSTRLEN];
+ char xval[MAXXFRMLEN];
+} cstr;
+
+ATF_TC_WITHOUT_HEAD(strcoll_vs_strxfrm);
+ATF_TC_BODY(strcoll_vs_strxfrm, tc)
+{
+ cstr data[NSTRINGS];
+ char *curloc;
+ int i, j;
+
+ curloc = setlocale(LC_ALL, "en_US.UTF-8");
+ ATF_CHECK_MSG(curloc != NULL, "Fail to set locale");
+
+ /* Ensure new random() values on every run */
+ srandom((unsigned int) time(NULL));
+
+ /* Generate random UTF8 strings of length less than MAXSTRLEN bytes */
+ for (i = 0; i < NSTRINGS; i++) {
+ char *p;
+ int len;
+
+again:
+ p = data[i].sval;
+ len = 1 + (random() % (MAXSTRLEN - 1));
+ while (len > 0) {
+ int c;
+ /*
+ * Generate random printable char in ISO8859-1 range.
+ * Bias towards producing a lot of spaces.
+ */
+
+ if ((random() % 16) < 3) {
+ c = ' ';
+ } else {
+ do {
+ c = random() & 0xFF;
+ } while (!((c >= ' ' && c <= 127) ||
+ (c >= 0xA0 && c <= 0xFF)));
+ }
+
+ if (c <= 127) {
+ *p++ = c;
+ len--;
+ } else {
+ if (len < 2)
+ break;
+ /* Poor man's utf8-ification */
+ *p++ = 0xC0 + (c >> 6);
+ len--;
+ *p++ = 0x80 + (c & 0x3F);
+ len--;
+ }
+ }
+ *p = '\0';
+ /* strxfrm() each string as we produce it */
+ errno = 0;
+ ATF_CHECK_MSG(strxfrm(data[i].xval, data[i].sval,
+ MAXXFRMLEN) < MAXXFRMLEN, "strxfrm() result for %d-length "
+ " string exceeded %d bytes", (int)strlen(data[i].sval),
+ MAXXFRMLEN);
+
+ /*
+ * Amend strxfrm() failing on certain characters to be fixed and
+ * test later
+ */
+ if (errno != 0)
+ goto again;
+ }
+
+ for (i = 0; i < NSTRINGS; i++) {
+ for (j = 0; j < NSTRINGS; j++) {
+ int sr = strcoll(data[i].sval, data[j].sval);
+ int sx = strcmp(data[i].xval, data[j].xval);
+
+ ATF_CHECK_MSG(!((sr * sx < 0) ||
+ (sr * sx == 0 && sr + sx != 0)),
+ "%s: diff for \"%s\" and \"%s\"",
+ curloc, data[i].sval, data[j].sval);
+ }
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, russian_collation);
+ ATF_TP_ADD_TC(tp, strcoll_vs_strxfrm);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/string/wcsnlen_test.c b/lib/libc/tests/string/wcsnlen_test.c
new file mode 100644
index 000000000000..ba2e1c8d7c57
--- /dev/null
+++ b/lib/libc/tests/string/wcsnlen_test.c
@@ -0,0 +1,104 @@
+/*-
+ * Copyright (c) 2009 David Schultz <das@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#include <atf-c.h>
+
+static void *
+makebuf(size_t len, int guard_at_end)
+{
+ char *buf;
+ size_t alloc_size, page_size;
+
+ page_size = getpagesize();
+ alloc_size = roundup2(len, page_size) + page_size;
+
+ buf = mmap(NULL, alloc_size, PROT_READ | PROT_WRITE, MAP_ANON, -1, 0);
+ ATF_CHECK(buf);
+ if (guard_at_end) {
+ ATF_CHECK(munmap(buf + alloc_size - page_size, page_size) == 0);
+ return (buf + alloc_size - page_size - len);
+ } else {
+ ATF_CHECK(munmap(buf, page_size) == 0);
+ return (buf + page_size);
+ }
+}
+
+static void
+test_wcsnlen(const wchar_t *s)
+{
+ wchar_t *s1;
+ size_t size, len, bufsize;
+ int i;
+
+ size = wcslen(s) + 1;
+ for (i = 0; i <= 1; i++) {
+ for (bufsize = 0; bufsize <= size + 10; bufsize++) {
+ s1 = makebuf(bufsize * sizeof(wchar_t), i);
+ wmemcpy(s1, s, bufsize <= size ? bufsize : size);
+ len = (size > bufsize) ? bufsize : size - 1;
+ ATF_CHECK(wcsnlen(s1, bufsize) == len);
+ }
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(nul);
+ATF_TC_BODY(nul, tc)
+{
+
+ test_wcsnlen(L"");
+}
+
+ATF_TC_WITHOUT_HEAD(foo);
+ATF_TC_BODY(foo, tc)
+{
+
+ test_wcsnlen(L"foo");
+}
+
+ATF_TC_WITHOUT_HEAD(glorp);
+ATF_TC_BODY(glorp, tc)
+{
+
+ test_wcsnlen(L"glorp");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, nul);
+ ATF_TP_ADD_TC(tp, foo);
+ ATF_TP_ADD_TC(tp, glorp);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/sys/Makefile b/lib/libc/tests/sys/Makefile
new file mode 100644
index 000000000000..88f8191a16eb
--- /dev/null
+++ b/lib/libc/tests/sys/Makefile
@@ -0,0 +1,105 @@
+PACKAGE= tests
+
+.include <bsd.own.mk>
+
+.if ${MACHINE_CPUARCH} != "aarch64" && ${MACHINE_CPUARCH} != "riscv"
+ATF_TESTS_C+= brk_test
+.endif
+ATF_TESTS_C+= cpuset_test
+ATF_TESTS_C+= errno_test
+ATF_TESTS_C+= swapcontext_test
+ATF_TESTS_C+= queue_test
+ATF_TESTS_C+= sendfile_test
+
+# TODO: clone, lwp_create, lwp_ctl, posix_fadvise, recvmmsg
+NETBSD_ATF_TESTS_C+= access_test
+NETBSD_ATF_TESTS_C+= bind_test
+NETBSD_ATF_TESTS_C+= chroot_test
+NETBSD_ATF_TESTS_C+= clock_gettime_test
+NETBSD_ATF_TESTS_C+= clock_nanosleep_test
+NETBSD_ATF_TESTS_C+= connect_test
+NETBSD_ATF_TESTS_C+= dup_test
+NETBSD_ATF_TESTS_C+= fsync_test
+NETBSD_ATF_TESTS_C+= getcontext_test
+NETBSD_ATF_TESTS_C+= getgroups_test
+NETBSD_ATF_TESTS_C+= getitimer_test
+NETBSD_ATF_TESTS_C+= getlogin_test
+NETBSD_ATF_TESTS_C+= getpid_test
+NETBSD_ATF_TESTS_C+= getrusage_test
+NETBSD_ATF_TESTS_C+= getsid_test
+NETBSD_ATF_TESTS_C+= getsockname_test
+NETBSD_ATF_TESTS_C+= gettimeofday_test
+NETBSD_ATF_TESTS_C+= issetugid_test
+NETBSD_ATF_TESTS_C+= kevent_test
+NETBSD_ATF_TESTS_C+= kill_test
+NETBSD_ATF_TESTS_C+= link_test
+NETBSD_ATF_TESTS_C+= listen_test
+NETBSD_ATF_TESTS_C+= mincore_test
+NETBSD_ATF_TESTS_C+= mkdir_test
+NETBSD_ATF_TESTS_C+= mkfifo_test
+NETBSD_ATF_TESTS_C+= mknod_test
+NETBSD_ATF_TESTS_C+= mlock_test
+NETBSD_ATF_TESTS_C+= mmap_test
+NETBSD_ATF_TESTS_C+= mprotect_test
+NETBSD_ATF_TESTS_C+= msgctl_test
+NETBSD_ATF_TESTS_C+= msgget_test
+NETBSD_ATF_TESTS_C+= msgrcv_test
+NETBSD_ATF_TESTS_C+= msgsnd_test
+NETBSD_ATF_TESTS_C+= msync_test
+NETBSD_ATF_TESTS_C+= nanosleep_test
+NETBSD_ATF_TESTS_C+= pipe_test
+NETBSD_ATF_TESTS_C+= pipe2_test
+NETBSD_ATF_TESTS_C+= poll_test
+NETBSD_ATF_TESTS_C+= posix_fallocate_test
+NETBSD_ATF_TESTS_C+= revoke_test
+NETBSD_ATF_TESTS_C+= select_test
+NETBSD_ATF_TESTS_C+= setrlimit_test
+NETBSD_ATF_TESTS_C+= setuid_test
+NETBSD_ATF_TESTS_C+= sigaction_test
+NETBSD_ATF_TESTS_C+= sigqueue_test
+NETBSD_ATF_TESTS_C+= sigtimedwait_test
+NETBSD_ATF_TESTS_C+= socketpair_test
+NETBSD_ATF_TESTS_C+= stat_test
+NETBSD_ATF_TESTS_C+= timer_create_test
+NETBSD_ATF_TESTS_C+= truncate_test
+NETBSD_ATF_TESTS_C+= ucontext_test
+NETBSD_ATF_TESTS_C+= umask_test
+NETBSD_ATF_TESTS_C+= unlink_test
+NETBSD_ATF_TESTS_C+= wait_test
+NETBSD_ATF_TESTS_C+= wait_noproc_test
+NETBSD_ATF_TESTS_C+= wait_noproc_wnohang_test
+NETBSD_ATF_TESTS_C+= write_test
+
+LIBADD.getpid_test+= pthread
+LIBADD.timer_create_test+= rt
+
+# Message queue IPC tests need to be executed serially since they variously
+# use global keys and exhaust global IPC limits.
+TEST_METADATA.msgctl_test+= is_exclusive="true"
+TEST_METADATA.msgget_test+= is_exclusive="true"
+TEST_METADATA.msgsnd_test+= is_exclusive="true"
+TEST_METADATA.msgrcv_test+= is_exclusive="true"
+
+.include "../Makefile.netbsd-tests"
+
+SRCS.mlock_test+= mlock_helper.c
+SRCS.setrlimit_test+= mlock_helper.c
+
+FILESGROUPS+= truncate_test_FILES
+
+truncate_test_FILES= truncate_test.root_owned
+truncate_test_FILESDIR= ${TESTSDIR}
+truncate_test_FILESMODE= 0600
+truncate_test_FILESOWN= root
+truncate_test_FILESGRP= wheel
+truncate_test_FILESPACKAGE= ${PACKAGE}
+
+CLEANFILES= truncate_test.root_owned
+# The dd status=none option is non-standard. Only use it when this test succeeds
+# rather than require dd to be a bootstrap tool.
+DD_NOSTATUS!=(dd status=none count=0 2> /dev/null && echo status=none) || true
+DD=dd ${DD_NOSTATUS}
+truncate_test.root_owned:
+ ${DD} if=/dev/null bs=1 count=1 of=${.TARGET}
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/sys/Makefile.depend b/lib/libc/tests/sys/Makefile.depend
new file mode 100644
index 000000000000..c9d1296c4e9c
--- /dev/null
+++ b/lib/libc/tests/sys/Makefile.depend
@@ -0,0 +1,22 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/arpa \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libkvm \
+ lib/libnetbsd \
+ lib/librt \
+ lib/libthr \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/sys/brk_test.c b/lib/libc/tests/sys/brk_test.c
new file mode 100644
index 000000000000..2d8c7af38ff7
--- /dev/null
+++ b/lib/libc/tests/sys/brk_test.c
@@ -0,0 +1,146 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018 Mark Johnston <markj@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+ATF_TC(brk_basic);
+ATF_TC_HEAD(brk_basic, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Verify basic brk() functionality");
+}
+ATF_TC_BODY(brk_basic, tc)
+{
+ void *oldbrk, *newbrk;
+ int error;
+
+ /* Reset the break. */
+ error = brk(0);
+ ATF_REQUIRE_MSG(error == 0, "brk: %s", strerror(errno));
+
+ oldbrk = sbrk(0);
+ ATF_REQUIRE(oldbrk != (void *)-1);
+
+ /* Try to allocate a page. */
+ error = brk((void *)((intptr_t)oldbrk + PAGE_SIZE * 2));
+ ATF_REQUIRE_MSG(error == 0, "brk: %s", strerror(errno));
+
+ /*
+ * Attempt to set the break below minbrk. This should have no effect.
+ */
+ error = brk((void *)((intptr_t)oldbrk - 1));
+ ATF_REQUIRE_MSG(error == 0, "brk: %s", strerror(errno));
+ newbrk = sbrk(0);
+ ATF_REQUIRE_MSG(newbrk != (void *)-1, "sbrk: %s", strerror(errno));
+ ATF_REQUIRE(newbrk == oldbrk);
+}
+
+ATF_TC(sbrk_basic);
+ATF_TC_HEAD(sbrk_basic, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Verify basic sbrk() functionality");
+}
+ATF_TC_BODY(sbrk_basic, tc)
+{
+ void *newbrk, *oldbrk;
+ int *p;
+
+ oldbrk = sbrk(0);
+ ATF_REQUIRE_MSG(oldbrk != (void *)-1, "sbrk: %s", strerror(errno));
+ p = sbrk(sizeof(*p));
+ *p = 0;
+ ATF_REQUIRE(oldbrk == p);
+
+ newbrk = sbrk(-sizeof(*p));
+ ATF_REQUIRE_MSG(newbrk != (void *)-1, "sbrk: %s", strerror(errno));
+ ATF_REQUIRE(oldbrk == sbrk(0));
+
+ oldbrk = sbrk(PAGE_SIZE * 2 + 1);
+ ATF_REQUIRE_MSG(oldbrk != (void *)-1, "sbrk: %s", strerror(errno));
+ memset(oldbrk, 0, PAGE_SIZE * 2 + 1);
+ newbrk = sbrk(-(PAGE_SIZE * 2 + 1));
+ ATF_REQUIRE_MSG(newbrk != (void *)-1, "sbrk: %s", strerror(errno));
+ ATF_REQUIRE(sbrk(0) == oldbrk);
+}
+
+ATF_TC(mlockfuture);
+ATF_TC_HEAD(mlockfuture, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that mlockall(MCL_FUTURE) applies to the data segment");
+}
+ATF_TC_BODY(mlockfuture, tc)
+{
+ void *oldbrk, *n, *newbrk;
+ int error;
+ char v;
+
+ error = mlockall(MCL_FUTURE);
+ ATF_REQUIRE_MSG(error == 0,
+ "mlockall: %s", strerror(errno));
+
+ /*
+ * Advance the break so that at least one page is added to the data
+ * segment. This page should be automatically faulted in to the address
+ * space.
+ */
+ oldbrk = sbrk(0);
+ ATF_REQUIRE(oldbrk != (void *)-1);
+ newbrk = sbrk(PAGE_SIZE * 2);
+ ATF_REQUIRE(newbrk != (void *)-1);
+
+ n = (void *)(((uintptr_t)oldbrk + PAGE_SIZE) & ~PAGE_SIZE);
+ v = 0;
+ error = mincore(n, PAGE_SIZE, &v);
+ ATF_REQUIRE_MSG(error == 0,
+ "mincore: %s", strerror(errno));
+ ATF_REQUIRE_MSG((v & MINCORE_INCORE) != 0,
+ "unexpected page flags %#x", v);
+
+ error = brk(oldbrk);
+ ATF_REQUIRE(error == 0);
+
+ error = munlockall();
+ ATF_REQUIRE_MSG(error == 0,
+ "munlockall: %s", strerror(errno));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, brk_basic);
+ ATF_TP_ADD_TC(tp, sbrk_basic);
+ ATF_TP_ADD_TC(tp, mlockfuture);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/sys/cpuset_test.c b/lib/libc/tests/sys/cpuset_test.c
new file mode 100644
index 000000000000..53d6a8215bbc
--- /dev/null
+++ b/lib/libc/tests/sys/cpuset_test.c
@@ -0,0 +1,691 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2020-2021 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/cpuset.h>
+#include <sys/jail.h>
+#include <sys/procdesc.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#define SP_PARENT 0
+#define SP_CHILD 1
+
+struct jail_test_info {
+ cpuset_t jail_tidmask;
+ cpusetid_t jail_cpuset;
+ cpusetid_t jail_child_cpuset;
+};
+
+struct jail_test_cb_params {
+ struct jail_test_info info;
+ cpuset_t mask;
+ cpusetid_t rootid;
+ cpusetid_t setid;
+};
+
+typedef void (*jail_test_cb)(struct jail_test_cb_params *);
+
+#define FAILURE_JAIL 42
+#define FAILURE_MASK 43
+#define FAILURE_JAILSET 44
+#define FAILURE_PIDSET 45
+#define FAILURE_SEND 46
+#define FAILURE_DEADLK 47
+#define FAILURE_ATTACH 48
+#define FAILURE_BADAFFIN 49
+#define FAILURE_SUCCESS 50
+
+static const char *
+do_jail_errstr(int error)
+{
+
+ switch (error) {
+ case FAILURE_JAIL:
+ return ("jail_set(2) failed");
+ case FAILURE_MASK:
+ return ("Failed to get the thread cpuset mask");
+ case FAILURE_JAILSET:
+ return ("Failed to get the jail setid");
+ case FAILURE_PIDSET:
+ return ("Failed to get the pid setid");
+ case FAILURE_SEND:
+ return ("Failed to send(2) cpuset information");
+ case FAILURE_DEADLK:
+ return ("Deadlock hit trying to attach to jail");
+ case FAILURE_ATTACH:
+ return ("jail_attach(2) failed");
+ case FAILURE_BADAFFIN:
+ return ("Unexpected post-attach affinity");
+ case FAILURE_SUCCESS:
+ return ("jail_attach(2) succeeded, but should have failed.");
+ default:
+ return (NULL);
+ }
+}
+
+static void
+skip_ltncpu(int ncpu, cpuset_t *mask)
+{
+
+ CPU_ZERO(mask);
+ ATF_REQUIRE_EQ(0, cpuset_getaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID,
+ -1, sizeof(*mask), mask));
+ if (CPU_COUNT(mask) < ncpu)
+ atf_tc_skip("Test requires %d or more cores.", ncpu);
+}
+
+ATF_TC(newset);
+ATF_TC_HEAD(newset, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test cpuset(2)");
+}
+ATF_TC_BODY(newset, tc)
+{
+ cpusetid_t nsetid, setid, qsetid;
+
+ /* Obtain our initial set id. */
+ ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_TID, -1,
+ &setid));
+
+ /* Create a new one. */
+ ATF_REQUIRE_EQ(0, cpuset(&nsetid));
+ ATF_CHECK(nsetid != setid);
+
+ /* Query id again, make sure it's equal to the one we just got. */
+ ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_TID, -1,
+ &qsetid));
+ ATF_CHECK_EQ(nsetid, qsetid);
+}
+
+ATF_TC(transient);
+ATF_TC_HEAD(transient, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test that transient cpusets are freed.");
+}
+ATF_TC_BODY(transient, tc)
+{
+ cpusetid_t isetid, scratch, setid;
+
+ ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_PID, -1,
+ &isetid));
+
+ ATF_REQUIRE_EQ(0, cpuset(&setid));
+ ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET,
+ setid, &scratch));
+
+ /*
+ * Return back to our initial cpuset; the kernel should free the cpuset
+ * we just created.
+ */
+ ATF_REQUIRE_EQ(0, cpuset_setid(CPU_WHICH_PID, -1, isetid));
+ ATF_REQUIRE_EQ(-1, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET,
+ setid, &scratch));
+ ATF_CHECK_EQ(ESRCH, errno);
+}
+
+ATF_TC(deadlk);
+ATF_TC_HEAD(deadlk, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test against disjoint cpusets.");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(deadlk, tc)
+{
+ cpusetid_t setid;
+ cpuset_t dismask, mask, omask;
+ int fcpu, i, found, ncpu, second;
+
+ /* Make sure we have 3 cpus, so we test partial overlap. */
+ skip_ltncpu(3, &omask);
+
+ ATF_REQUIRE_EQ(0, cpuset(&setid));
+ CPU_ZERO(&mask);
+ CPU_ZERO(&dismask);
+ CPU_COPY(&omask, &mask);
+ CPU_COPY(&omask, &dismask);
+ fcpu = CPU_FFS(&mask);
+ ncpu = CPU_COUNT(&mask);
+
+ /*
+ * Turn off all but the first two for mask, turn off the first for
+ * dismask and turn them all off for both after the third.
+ */
+ for (i = fcpu - 1, found = 0; i < CPU_MAXSIZE && found != ncpu; i++) {
+ if (CPU_ISSET(i, &omask)) {
+ found++;
+ if (found == 1) {
+ CPU_CLR(i, &dismask);
+ } else if (found == 2) {
+ second = i;
+ } else if (found >= 3) {
+ CPU_CLR(i, &mask);
+ if (found > 3)
+ CPU_CLR(i, &dismask);
+ }
+ }
+ }
+
+ ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID,
+ -1, sizeof(mask), &mask));
+
+ /* Must be a strict subset! */
+ ATF_REQUIRE_EQ(-1, cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
+ -1, sizeof(dismask), &dismask));
+ ATF_REQUIRE_EQ(EINVAL, errno);
+
+ /*
+ * We'll set our anonymous set to the 0,1 set that currently matches
+ * the process. If we then set the process to the 1,2 set that's in
+ * dismask, we should then personally be restricted down to the single
+ * overlapping CPOU.
+ */
+ ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
+ -1, sizeof(mask), &mask));
+ ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID,
+ -1, sizeof(dismask), &dismask));
+ ATF_REQUIRE_EQ(0, cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
+ -1, sizeof(mask), &mask));
+ ATF_REQUIRE_EQ(1, CPU_COUNT(&mask));
+ ATF_REQUIRE(CPU_ISSET(second, &mask));
+
+ /*
+ * Finally, clearing the overlap and attempting to set the process
+ * cpuset to a completely disjoint mask should fail, because this
+ * process will then not have anything to run on.
+ */
+ CPU_CLR(second, &dismask);
+ ATF_REQUIRE_EQ(-1, cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID,
+ -1, sizeof(dismask), &dismask));
+ ATF_REQUIRE_EQ(EDEADLK, errno);
+}
+
+static int
+do_jail(int sock)
+{
+ struct jail_test_info info;
+ struct iovec iov[2];
+ char *name;
+ int error;
+
+ if (asprintf(&name, "cpuset_%d", getpid()) == -1)
+ _exit(42);
+
+ iov[0].iov_base = "name";
+ iov[0].iov_len = 5;
+
+ iov[1].iov_base = name;
+ iov[1].iov_len = strlen(name) + 1;
+
+ if (jail_set(iov, 2, JAIL_CREATE | JAIL_ATTACH) < 0)
+ return (FAILURE_JAIL);
+
+ /* Record parameters, kick them over, then make a swift exit. */
+ CPU_ZERO(&info.jail_tidmask);
+ error = cpuset_getaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
+ -1, sizeof(info.jail_tidmask), &info.jail_tidmask);
+ if (error != 0)
+ return (FAILURE_MASK);
+
+ error = cpuset_getid(CPU_LEVEL_ROOT, CPU_WHICH_TID, -1,
+ &info.jail_cpuset);
+ if (error != 0)
+ return (FAILURE_JAILSET);
+ error = cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_TID, -1,
+ &info.jail_child_cpuset);
+ if (error != 0)
+ return (FAILURE_PIDSET);
+ if (send(sock, &info, sizeof(info), 0) != sizeof(info))
+ return (FAILURE_SEND);
+ return (0);
+}
+
+static void
+do_jail_test(int ncpu, bool newset, jail_test_cb prologue,
+ jail_test_cb epilogue)
+{
+ struct jail_test_cb_params cbp;
+ const char *errstr;
+ pid_t pid;
+ int error, sock, sockpair[2], status;
+
+ memset(&cbp.info, '\0', sizeof(cbp.info));
+
+ skip_ltncpu(ncpu, &cbp.mask);
+
+ ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_ROOT, CPU_WHICH_PID, -1,
+ &cbp.rootid));
+ if (newset)
+ ATF_REQUIRE_EQ(0, cpuset(&cbp.setid));
+ else
+ ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_PID,
+ -1, &cbp.setid));
+ /* Special hack for prison0; it uses cpuset 1 as the root. */
+ if (cbp.rootid == 0)
+ cbp.rootid = 1;
+
+ /* Not every test needs early setup. */
+ if (prologue != NULL)
+ (*prologue)(&cbp);
+
+ ATF_REQUIRE_EQ(0, socketpair(PF_UNIX, SOCK_STREAM, 0, sockpair));
+ ATF_REQUIRE((pid = fork()) != -1);
+
+ if (pid == 0) {
+ /* Child */
+ close(sockpair[SP_PARENT]);
+ sock = sockpair[SP_CHILD];
+
+ _exit(do_jail(sock));
+ } else {
+ /* Parent */
+ sock = sockpair[SP_PARENT];
+ close(sockpair[SP_CHILD]);
+
+ while ((error = waitpid(pid, &status, 0)) == -1 &&
+ errno == EINTR) {
+ }
+
+ ATF_REQUIRE_EQ(sizeof(cbp.info), recv(sock, &cbp.info,
+ sizeof(cbp.info), 0));
+
+ /* Sanity check the exit info. */
+ ATF_REQUIRE_EQ(pid, error);
+ ATF_REQUIRE(WIFEXITED(status));
+ if (WEXITSTATUS(status) != 0) {
+ errstr = do_jail_errstr(WEXITSTATUS(status));
+ if (errstr != NULL)
+ atf_tc_fail("%s", errstr);
+ else
+ atf_tc_fail("Unknown error '%d'",
+ WEXITSTATUS(status));
+ }
+
+ epilogue(&cbp);
+ }
+}
+
+static void
+jail_attach_mutate_pro(struct jail_test_cb_params *cbp)
+{
+ cpuset_t *mask;
+ int count;
+
+ mask = &cbp->mask;
+
+ /* Knock out the first cpu. */
+ count = CPU_COUNT(mask);
+ CPU_CLR(CPU_FFS(mask) - 1, mask);
+ ATF_REQUIRE_EQ(count - 1, CPU_COUNT(mask));
+ ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
+ -1, sizeof(*mask), mask));
+}
+
+static void
+jail_attach_newbase_epi(struct jail_test_cb_params *cbp)
+{
+ struct jail_test_info *info;
+ cpuset_t *mask;
+
+ info = &cbp->info;
+ mask = &cbp->mask;
+
+ /*
+ * The rootid test has been thrown in because a bug was discovered
+ * where any newly derived cpuset during attach would be parented to
+ * the wrong cpuset. Otherwise, we should observe that a new cpuset
+ * has been created for this process.
+ */
+ ATF_REQUIRE(info->jail_cpuset != cbp->rootid);
+ ATF_REQUIRE(info->jail_cpuset != cbp->setid);
+ ATF_REQUIRE(info->jail_cpuset != info->jail_child_cpuset);
+ ATF_REQUIRE_EQ(0, CPU_CMP(mask, &info->jail_tidmask));
+}
+
+ATF_TC(jail_attach_newbase);
+ATF_TC_HEAD(jail_attach_newbase, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test jail attachment effect on affinity with a new base cpuset.");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(jail_attach_newbase, tc)
+{
+
+ /* Need >= 2 cpus to test restriction. */
+ do_jail_test(2, true, &jail_attach_mutate_pro,
+ &jail_attach_newbase_epi);
+}
+
+ATF_TC(jail_attach_newbase_plain);
+ATF_TC_HEAD(jail_attach_newbase_plain, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test jail attachment effect on affinity with a new, unmodified base cpuset.");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(jail_attach_newbase_plain, tc)
+{
+
+ do_jail_test(2, true, NULL, &jail_attach_newbase_epi);
+}
+
+/*
+ * Generic epilogue for tests that are expecting to use the jail's root cpuset
+ * with their own mask, whether that's been modified or not.
+ */
+static void
+jail_attach_jset_epi(struct jail_test_cb_params *cbp)
+{
+ struct jail_test_info *info;
+ cpuset_t *mask;
+
+ info = &cbp->info;
+ mask = &cbp->mask;
+
+ ATF_REQUIRE(info->jail_cpuset != cbp->setid);
+ ATF_REQUIRE_EQ(info->jail_cpuset, info->jail_child_cpuset);
+ ATF_REQUIRE_EQ(0, CPU_CMP(mask, &info->jail_tidmask));
+}
+
+ATF_TC(jail_attach_prevbase);
+ATF_TC_HEAD(jail_attach_prevbase, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test jail attachment effect on affinity without a new base.");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(jail_attach_prevbase, tc)
+{
+
+ do_jail_test(2, false, &jail_attach_mutate_pro, &jail_attach_jset_epi);
+}
+
+static void
+jail_attach_plain_pro(struct jail_test_cb_params *cbp)
+{
+
+ if (cbp->setid != cbp->rootid)
+ atf_tc_skip("Must be running with the root cpuset.");
+}
+
+ATF_TC(jail_attach_plain);
+ATF_TC_HEAD(jail_attach_plain, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test jail attachment effect on affinity without specialization.");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(jail_attach_plain, tc)
+{
+
+ do_jail_test(1, false, &jail_attach_plain_pro, &jail_attach_jset_epi);
+}
+
+static int
+jail_attach_disjoint_newjail(int fd)
+{
+ struct iovec iov[2];
+ char *name;
+ int jid;
+
+ if (asprintf(&name, "cpuset_%d", getpid()) == -1)
+ _exit(42);
+
+ iov[0].iov_base = "name";
+ iov[0].iov_len = sizeof("name");
+
+ iov[1].iov_base = name;
+ iov[1].iov_len = strlen(name) + 1;
+
+ if ((jid = jail_set(iov, 2, JAIL_CREATE | JAIL_ATTACH)) < 0)
+ return (FAILURE_JAIL);
+
+ /* Signal that we're ready. */
+ write(fd, &jid, sizeof(jid));
+ for (;;) {
+ /* Spin */
+ }
+}
+
+static int
+wait_jail(int fd, int pfd)
+{
+ fd_set lset;
+ struct timeval tv;
+ int error, jid, maxfd;
+
+ FD_ZERO(&lset);
+ FD_SET(fd, &lset);
+ FD_SET(pfd, &lset);
+
+ maxfd = MAX(fd, pfd);
+
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+
+ /* Wait for jid to be written. */
+ do {
+ error = select(maxfd + 1, &lset, NULL, NULL, &tv);
+ } while (error == -1 && errno == EINTR);
+
+ if (error == 0) {
+ atf_tc_fail("Jail creator did not respond in time.");
+ }
+
+ ATF_REQUIRE_MSG(error > 0, "Unexpected error %d from select()", errno);
+
+ if (FD_ISSET(pfd, &lset)) {
+ /* Process died */
+ atf_tc_fail("Jail creator died unexpectedly.");
+ }
+
+ ATF_REQUIRE(FD_ISSET(fd, &lset));
+ ATF_REQUIRE_EQ(sizeof(jid), recv(fd, &jid, sizeof(jid), 0));
+
+ return (jid);
+}
+
+static int
+try_attach_child(int jid, cpuset_t *expected_mask)
+{
+ cpuset_t mask;
+
+ if (jail_attach(jid) == -1) {
+ if (errno == EDEADLK)
+ return (FAILURE_DEADLK);
+ return (FAILURE_ATTACH);
+ }
+
+ if (expected_mask == NULL)
+ return (FAILURE_SUCCESS);
+
+ /* If we had an expected mask, check it against the new process mask. */
+ CPU_ZERO(&mask);
+ if (cpuset_getaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_PID,
+ -1, sizeof(mask), &mask) != 0) {
+ return (FAILURE_MASK);
+ }
+
+ if (CPU_CMP(expected_mask, &mask) != 0)
+ return (FAILURE_BADAFFIN);
+
+ return (0);
+}
+
+static void
+try_attach(int jid, cpuset_t *expected_mask)
+{
+ const char *errstr;
+ pid_t pid;
+ int error, fail, status;
+
+ ATF_REQUIRE(expected_mask != NULL);
+ ATF_REQUIRE((pid = fork()) != -1);
+ if (pid == 0)
+ _exit(try_attach_child(jid, expected_mask));
+
+ while ((error = waitpid(pid, &status, 0)) == -1 && errno == EINTR) {
+ /* Try again. */
+ }
+
+ /* Sanity check the exit info. */
+ ATF_REQUIRE_EQ(pid, error);
+ ATF_REQUIRE(WIFEXITED(status));
+ if ((fail = WEXITSTATUS(status)) != 0) {
+ errstr = do_jail_errstr(fail);
+ if (errstr != NULL)
+ atf_tc_fail("%s", errstr);
+ else
+ atf_tc_fail("Unknown error '%d'", WEXITSTATUS(status));
+ }
+}
+
+ATF_TC(jail_attach_disjoint);
+ATF_TC_HEAD(jail_attach_disjoint, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test root attachment into completely disjoint jail cpuset.");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(jail_attach_disjoint, tc)
+{
+ cpuset_t smask, jmask;
+ int sockpair[2];
+ cpusetid_t setid;
+ pid_t pid;
+ int fcpu, jid, pfd, sock, scpu;
+
+ ATF_REQUIRE_EQ(0, cpuset(&setid));
+
+ skip_ltncpu(2, &jmask);
+ fcpu = CPU_FFS(&jmask) - 1;
+ ATF_REQUIRE_EQ(0, socketpair(PF_UNIX, SOCK_STREAM, 0, sockpair));
+
+ /* We'll wait on the procdesc, too, so we can fail faster if it dies. */
+ ATF_REQUIRE((pid = pdfork(&pfd, 0)) != -1);
+
+ if (pid == 0) {
+ /* First child sets up the jail. */
+ sock = sockpair[SP_CHILD];
+ close(sockpair[SP_PARENT]);
+
+ _exit(jail_attach_disjoint_newjail(sock));
+ }
+
+ close(sockpair[SP_CHILD]);
+ sock = sockpair[SP_PARENT];
+
+ ATF_REQUIRE((jid = wait_jail(sock, pfd)) > 0);
+
+ /*
+ * This process will be clamped down to the first cpu, while the jail
+ * will simply have the first CPU removed to make it a completely
+ * disjoint operation.
+ */
+ CPU_ZERO(&smask);
+ CPU_SET(fcpu, &smask);
+ CPU_CLR(fcpu, &jmask);
+
+ /*
+ * We'll test with the first and second cpu set as well. Only the
+ * second cpu should be used.
+ */
+ scpu = CPU_FFS(&jmask) - 1;
+
+ ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_ROOT, CPU_WHICH_JAIL,
+ jid, sizeof(jmask), &jmask));
+ ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET,
+ setid, sizeof(smask), &smask));
+
+ try_attach(jid, &jmask);
+
+ CPU_SET(scpu, &smask);
+ ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_CPUSET, CPU_WHICH_CPUSET,
+ setid, sizeof(smask), &smask));
+
+ CPU_CLR(fcpu, &smask);
+ try_attach(jid, &smask);
+}
+
+ATF_TC(badparent);
+ATF_TC_HEAD(badparent, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test parent assignment when assigning a new cpuset.");
+}
+ATF_TC_BODY(badparent, tc)
+{
+ cpuset_t mask;
+ cpusetid_t finalsetid, origsetid, setid;
+
+ /* Need to mask off at least one CPU. */
+ skip_ltncpu(2, &mask);
+
+ ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_TID, -1,
+ &origsetid));
+
+ ATF_REQUIRE_EQ(0, cpuset(&setid));
+
+ /*
+ * Mask off the first CPU, then we'll reparent ourselves to our original
+ * set.
+ */
+ CPU_CLR(CPU_FFS(&mask) - 1, &mask);
+ ATF_REQUIRE_EQ(0, cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID,
+ -1, sizeof(mask), &mask));
+
+ ATF_REQUIRE_EQ(0, cpuset_setid(CPU_WHICH_PID, -1, origsetid));
+ ATF_REQUIRE_EQ(0, cpuset_getid(CPU_LEVEL_CPUSET, CPU_WHICH_TID, -1,
+ &finalsetid));
+
+ ATF_REQUIRE_EQ(finalsetid, origsetid);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, newset);
+ ATF_TP_ADD_TC(tp, transient);
+ ATF_TP_ADD_TC(tp, deadlk);
+ ATF_TP_ADD_TC(tp, jail_attach_newbase);
+ ATF_TP_ADD_TC(tp, jail_attach_newbase_plain);
+ ATF_TP_ADD_TC(tp, jail_attach_prevbase);
+ ATF_TP_ADD_TC(tp, jail_attach_plain);
+ ATF_TP_ADD_TC(tp, jail_attach_disjoint);
+ ATF_TP_ADD_TC(tp, badparent);
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/sys/errno_test.c b/lib/libc/tests/sys/errno_test.c
new file mode 100644
index 000000000000..27d0548fc29d
--- /dev/null
+++ b/lib/libc/tests/sys/errno_test.c
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2024 The FreeBSD Foundation
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * This software were developed by Konstantin Belousov <kib@FreeBSD.org>
+ * under sponsorship from the FreeBSD Foundation.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+ATF_TC(errno_basic);
+ATF_TC_HEAD(errno_basic, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify basic functionality of errno");
+}
+
+ATF_TC_BODY(errno_basic, tc)
+{
+ int res;
+
+ res = unlink("/non/existent/file");
+ ATF_REQUIRE(res == -1);
+ ATF_REQUIRE(errno == ENOENT);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, errno_basic);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/sys/mlock_helper.c b/lib/libc/tests/sys/mlock_helper.c
new file mode 100644
index 000000000000..e7a3d5e39c3f
--- /dev/null
+++ b/lib/libc/tests/sys/mlock_helper.c
@@ -0,0 +1,111 @@
+/*-
+ * Copyright (C) 2016 Bryan Drewery <bdrewery@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Helper for mlock(3) to avoid EAGAIN errors
+ */
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+
+#define VM_MAX_WIRED "vm.max_user_wired"
+
+static void
+vm_max_wired_sysctl(u_long *old_value, u_long *new_value)
+{
+ size_t old_len;
+ size_t new_len = (new_value == NULL ? 0 : sizeof(*new_value));
+
+ if (old_value == NULL)
+ printf("Setting the new value to %lu\n", *new_value);
+ else {
+ ATF_REQUIRE_MSG(sysctlbyname(VM_MAX_WIRED, NULL, &old_len,
+ new_value, new_len) == 0,
+ "sysctlbyname(%s) failed: %s", VM_MAX_WIRED, strerror(errno));
+ }
+
+ ATF_REQUIRE_MSG(sysctlbyname(VM_MAX_WIRED, old_value, &old_len,
+ new_value, new_len) == 0,
+ "sysctlbyname(%s) failed: %s", VM_MAX_WIRED, strerror(errno));
+
+ if (old_value != NULL)
+ printf("Saved the old value (%lu)\n", *old_value);
+}
+
+void
+set_vm_max_wired(u_long new_value)
+{
+ FILE *fp;
+ u_long old_value;
+
+ fp = fopen(VM_MAX_WIRED, "w");
+ if (fp == NULL) {
+ atf_tc_skip("could not open %s for writing: %s",
+ VM_MAX_WIRED, strerror(errno));
+ return;
+ }
+
+ vm_max_wired_sysctl(&old_value, NULL);
+
+ ATF_REQUIRE_MSG(fprintf(fp, "%lu", old_value) > 0,
+ "saving %s failed", VM_MAX_WIRED);
+
+ fclose(fp);
+
+ vm_max_wired_sysctl(NULL, &new_value);
+}
+
+void
+restore_vm_max_wired(void)
+{
+ FILE *fp;
+ u_long saved_max_wired;
+
+ fp = fopen(VM_MAX_WIRED, "r");
+ if (fp == NULL) {
+ perror("fopen failed\n");
+ return;
+ }
+
+ if (fscanf(fp, "%lu", &saved_max_wired) != 1) {
+ perror("fscanf failed\n");
+ fclose(fp);
+ return;
+ }
+
+ fclose(fp);
+ printf("old value in %s: %lu\n", VM_MAX_WIRED, saved_max_wired);
+
+ if (saved_max_wired == 0) /* This will cripple the test host */
+ return;
+
+ vm_max_wired_sysctl(NULL, &saved_max_wired);
+}
diff --git a/lib/libc/tests/sys/queue_test.c b/lib/libc/tests/sys/queue_test.c
new file mode 100644
index 000000000000..cfe9ac934cbd
--- /dev/null
+++ b/lib/libc/tests/sys/queue_test.c
@@ -0,0 +1,234 @@
+/*-
+ * Copyright (c) 2015 EMC Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/queue.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <atf-c.h>
+
+ATF_TC(slist_test);
+ATF_TC_HEAD(slist_test, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "SLIST macro feature tests");
+}
+
+ATF_TC_BODY(slist_test, tc)
+{
+ SLIST_HEAD(stailhead, entry) head = SLIST_HEAD_INITIALIZER(head);
+ struct entry {
+ SLIST_ENTRY(entry) entries;
+ int i;
+ } *n1, *n2, *n3, *np;
+ int i, j, length;
+
+ SLIST_INIT(&head);
+
+ printf("Ensuring SLIST_EMPTY works\n");
+
+ ATF_REQUIRE(SLIST_EMPTY(&head));
+
+ i = length = 0;
+
+ SLIST_FOREACH(np, &head, entries) {
+ length++;
+ }
+ ATF_REQUIRE_EQ(length, 0);
+
+ printf("Ensuring SLIST_INSERT_HEAD works\n");
+
+ n1 = malloc(sizeof(struct entry));
+ ATF_REQUIRE(n1 != NULL);
+ n1->i = i++;
+
+ SLIST_INSERT_HEAD(&head, n1, entries);
+
+ printf("Ensuring SLIST_FIRST returns element 1\n");
+ ATF_REQUIRE_EQ(SLIST_FIRST(&head), n1);
+
+ j = length = 0;
+ SLIST_FOREACH(np, &head, entries) {
+ ATF_REQUIRE_EQ_MSG(np->i, j,
+ "%d (entry counter) != %d (counter)", np->i, j);
+ j++;
+ length++;
+ }
+ ATF_REQUIRE_EQ(length, 1);
+
+ printf("Ensuring SLIST_INSERT_AFTER works\n");
+
+ n2 = malloc(sizeof(struct entry));
+ ATF_REQUIRE(n2 != NULL);
+ n2->i = i++;
+
+ SLIST_INSERT_AFTER(n1, n2, entries);
+
+ n3 = malloc(sizeof(struct entry));
+ ATF_REQUIRE(n3 != NULL);
+ n3->i = i++;
+
+ SLIST_INSERT_AFTER(n2, n3, entries);
+
+ j = length = 0;
+ SLIST_FOREACH(np, &head, entries) {
+ ATF_REQUIRE_EQ_MSG(np->i, j,
+ "%d (entry counter) != %d (counter)", np->i, j);
+ j++;
+ length++;
+ }
+ ATF_REQUIRE_EQ(length, 3);
+
+ printf("Ensuring SLIST_REMOVE_HEAD works\n");
+
+ printf("Ensuring SLIST_FIRST returns element 1\n");
+ ATF_REQUIRE_EQ(SLIST_FIRST(&head), n1);
+
+ SLIST_REMOVE_HEAD(&head, entries);
+
+ printf("Ensuring SLIST_FIRST now returns element 2\n");
+ ATF_REQUIRE_EQ(SLIST_FIRST(&head), n2);
+
+ j = 1; /* Starting point's 1 this time */
+ length = 0;
+ SLIST_FOREACH(np, &head, entries) {
+ ATF_REQUIRE_EQ_MSG(np->i, j,
+ "%d (entry counter) != %d (counter)", np->i, j);
+ j++;
+ length++;
+ }
+ ATF_REQUIRE_EQ(length, 2);
+
+ printf("Ensuring SLIST_REMOVE_AFTER works by removing the tail\n");
+
+ SLIST_REMOVE_AFTER(n2, entries);
+
+ j = 1; /* Starting point's 1 this time */
+ length = 0;
+ SLIST_FOREACH(np, &head, entries) {
+ ATF_REQUIRE_EQ_MSG(np->i, j,
+ "%d (entry counter) != %d (counter)", np->i, j);
+ j++;
+ length++;
+ }
+ ATF_REQUIRE_EQ(length, 1);
+
+ printf("Ensuring SLIST_FIRST returns element 2\n");
+ ATF_REQUIRE_EQ(SLIST_FIRST(&head), n2);
+
+}
+
+ATF_TC(stailq_test);
+ATF_TC_HEAD(stailq_test, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "STAILQ macro feature tests");
+}
+
+ATF_TC_BODY(stailq_test, tc)
+{
+ STAILQ_HEAD(stailhead, entry) head = STAILQ_HEAD_INITIALIZER(head);
+ struct entry {
+ STAILQ_ENTRY(entry) entries;
+ int i;
+ } *n1, *n2, *n3, *np;
+ int i, j, length;
+
+ printf("Ensuring empty STAILQs are treated properly\n");
+ STAILQ_INIT(&head);
+ ATF_REQUIRE(STAILQ_EMPTY(&head));
+
+ i = length = 0;
+
+ STAILQ_FOREACH(np, &head, entries) {
+ length++;
+ }
+ ATF_REQUIRE_EQ(length, 0);
+
+ printf("Ensuring STAILQ_INSERT_HEAD works\n");
+
+ n1 = malloc(sizeof(struct entry));
+ ATF_REQUIRE(n1 != NULL);
+ n1->i = i++;
+
+ STAILQ_INSERT_HEAD(&head, n1, entries);
+
+ j = length = 0;
+ STAILQ_FOREACH(np, &head, entries) {
+ ATF_REQUIRE_EQ_MSG(np->i, j,
+ "%d (entry counter) != %d (counter)", np->i, j);
+ j++;
+ length++;
+ }
+ ATF_REQUIRE_EQ(length, 1);
+
+ printf("Ensuring STAILQ_INSERT_TAIL works\n");
+
+ n2 = malloc(sizeof(struct entry));
+ ATF_REQUIRE(n2 != NULL);
+ n2->i = i++;
+
+ STAILQ_INSERT_TAIL(&head, n2, entries);
+
+ n3 = malloc(sizeof(struct entry));
+ ATF_REQUIRE(n3 != NULL);
+ n3->i = i++;
+
+ STAILQ_INSERT_TAIL(&head, n3, entries);
+
+ j = length = 0;
+ STAILQ_FOREACH(np, &head, entries) {
+ ATF_REQUIRE_EQ_MSG(np->i, j,
+ "%d (entry counter) != %d (counter)", np->i, j);
+ j++;
+ length++;
+ }
+ ATF_REQUIRE_EQ(length, 3);
+
+ printf("Ensuring STAILQ_REMOVE_HEAD works\n");
+
+ STAILQ_REMOVE_HEAD(&head, entries);
+
+ j = 1; /* Starting point's 1 this time */
+ length = 0;
+ STAILQ_FOREACH(np, &head, entries) {
+ ATF_REQUIRE_EQ_MSG(np->i, j,
+ "%d (entry counter) != %d (counter)", np->i, j);
+ j++;
+ length++;
+ }
+ ATF_REQUIRE_EQ(length, 2);
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, slist_test);
+ ATF_TP_ADD_TC(tp, stailq_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/sys/sendfile_test.c b/lib/libc/tests/sys/sendfile_test.c
new file mode 100644
index 000000000000..d46e7b0cb186
--- /dev/null
+++ b/lib/libc/tests/sys/sendfile_test.c
@@ -0,0 +1,1208 @@
+/*-
+ * Copyright (c) 2018 Enji Cooper.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+const char DETERMINISTIC_PATTERN[] =
+ "The past is already gone, the future is not yet here. There's only one moment for you to live.\n";
+
+#define SOURCE_FILE "source"
+#define DESTINATION_FILE "dest"
+
+#define PORTRANGE_FIRST "net.inet.ip.portrange.first"
+#define PORTRANGE_LAST "net.inet.ip.portrange.last"
+
+static int portrange_first, portrange_last;
+
+static int
+get_int_via_sysctlbyname(const char *oidname)
+{
+ size_t oldlen;
+ int int_value;
+
+ oldlen = sizeof(int_value);
+
+ ATF_REQUIRE_EQ_MSG(sysctlbyname(oidname, &int_value, &oldlen, NULL, 0),
+ 0, "sysctlbyname(%s, ...) failed: %s", oidname, strerror(errno));
+ ATF_REQUIRE_EQ_MSG(sizeof(int_value), oldlen, "sanity check failed");
+
+ return (int_value);
+}
+
+static int
+generate_random_port(int seed)
+{
+ int random_port;
+
+ printf("Generating a random port with seed=%d\n", seed);
+ if (portrange_first == 0) {
+ portrange_first = get_int_via_sysctlbyname(PORTRANGE_FIRST);
+ printf("Port range lower bound: %d\n", portrange_first);
+ }
+
+ if (portrange_last == 0) {
+ portrange_last = get_int_via_sysctlbyname(PORTRANGE_LAST);
+ printf("Port range upper bound: %d\n", portrange_last);
+ }
+
+ srand((unsigned)seed);
+
+ random_port = rand() % (portrange_last - portrange_first) +
+ portrange_first;
+
+ printf("Random port generated: %d\n", random_port);
+ return (random_port);
+}
+
+static void
+resolve_localhost(struct addrinfo **res, int domain, int type, int port)
+{
+ const char *host;
+ char *serv;
+ struct addrinfo hints;
+ int error;
+
+ switch (domain) {
+ case AF_INET:
+ host = "127.0.0.1";
+ break;
+ case AF_INET6:
+ host = "::1";
+ break;
+ default:
+ atf_tc_fail("unhandled domain: %d", domain);
+ }
+
+ ATF_REQUIRE_MSG(asprintf(&serv, "%d", port) >= 0,
+ "asprintf failed: %s", strerror(errno));
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = domain;
+ hints.ai_flags = AI_ADDRCONFIG|AI_NUMERICSERV|AI_NUMERICHOST;
+ hints.ai_socktype = type;
+
+ error = getaddrinfo(host, serv, &hints, res);
+ ATF_REQUIRE_EQ_MSG(error, 0,
+ "getaddrinfo failed: %s", gai_strerror(error));
+ free(serv);
+}
+
+static int
+make_socket(int domain, int type, int protocol)
+{
+ int sock;
+
+ sock = socket(domain, type, protocol);
+ ATF_REQUIRE_MSG(sock != -1, "socket(%d, %d, 0) failed: %s",
+ domain, type, strerror(errno));
+
+ return (sock);
+}
+
+static int
+setup_client(int domain, int type, int port)
+{
+ struct addrinfo *res;
+ char host[NI_MAXHOST+1];
+ int error, sock;
+
+ resolve_localhost(&res, domain, type, port);
+ error = getnameinfo(
+ (const struct sockaddr*)res->ai_addr, res->ai_addrlen,
+ host, nitems(host) - 1, NULL, 0, NI_NUMERICHOST);
+ ATF_REQUIRE_EQ_MSG(error, 0,
+ "getnameinfo failed: %s", gai_strerror(error));
+ printf(
+ "Will try to connect to host='%s', address_family=%d, "
+ "socket_type=%d\n",
+ host, res->ai_family, res->ai_socktype);
+ /* Avoid a double print when forked by flushing. */
+ fflush(stdout);
+ sock = make_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ error = connect(sock, (struct sockaddr*)res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ ATF_REQUIRE_EQ_MSG(error, 0, "connect failed: %s", strerror(errno));
+ return (sock);
+}
+
+/*
+ * XXX: use linear probing to find a free port and eliminate `port` argument as
+ * a [const] int (it will need to be a pointer so it can be passed back out of
+ * the function and can influence which port `setup_client(..)` connects on.
+ */
+static int
+setup_server(int domain, int type, int port)
+{
+ struct addrinfo *res;
+ char host[NI_MAXHOST+1];
+ int error, sock;
+
+ resolve_localhost(&res, domain, type, port);
+ sock = make_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+
+ error = getnameinfo(
+ (const struct sockaddr*)res->ai_addr, res->ai_addrlen,
+ host, nitems(host) - 1, NULL, 0, NI_NUMERICHOST);
+ ATF_REQUIRE_EQ_MSG(error, 0,
+ "getnameinfo failed: %s", gai_strerror(error));
+ printf(
+ "Will try to bind socket to host='%s', address_family=%d, "
+ "socket_type=%d\n",
+ host, res->ai_family, res->ai_socktype);
+ /* Avoid a double print when forked by flushing. */
+ fflush(stdout);
+ error = bind(sock, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ ATF_REQUIRE_EQ_MSG(error, 0, "bind failed: %s", strerror(errno));
+ error = listen(sock, 1);
+ ATF_REQUIRE_EQ_MSG(error, 0, "listen failed: %s", strerror(errno));
+
+ return (sock);
+}
+
+/*
+ * This function is a helper routine for taking data being sent by `sendfile` via
+ * `server_sock`, and pushing the received stream out to a file, denoted by
+ * `dest_filename`.
+ */
+static void
+server_cat(const char *dest_filename, int server_sock, size_t len)
+{
+ char *buffer, *buf_window_ptr;
+ int recv_sock;
+ size_t buffer_size;
+ ssize_t received_bytes, recv_ret;
+
+ /*
+ * Ensure that there isn't excess data sent across the wire by
+ * capturing 10 extra bytes (plus 1 for nul).
+ */
+ buffer_size = len + 10 + 1;
+ buffer = calloc(buffer_size, sizeof(char));
+ if (buffer == NULL)
+ err(1, "malloc failed");
+
+ recv_sock = accept(server_sock, NULL, 0);
+ if (recv_sock == -1)
+ err(1, "accept failed");
+
+ buf_window_ptr = buffer;
+ received_bytes = 0;
+ do {
+ recv_ret = recv(recv_sock, buf_window_ptr,
+ buffer_size - received_bytes, 0);
+ if (recv_ret <= 0)
+ break;
+ buf_window_ptr += recv_ret;
+ received_bytes += recv_ret;
+ } while (received_bytes < buffer_size);
+
+ atf_utils_create_file(dest_filename, "%s", buffer);
+
+ (void)close(recv_sock);
+ (void)close(server_sock);
+ free(buffer);
+
+ if (received_bytes != len)
+ errx(1, "received unexpected data: %zd != %zd", received_bytes,
+ len);
+}
+
+static int
+setup_tcp_server(int domain, int port)
+{
+
+ return (setup_server(domain, SOCK_STREAM, port));
+}
+
+static int
+setup_tcp_client(int domain, int port)
+{
+
+ return (setup_client(domain, SOCK_STREAM, port));
+}
+
+static off_t
+file_size_from_fd(int fd)
+{
+ struct stat st;
+
+ ATF_REQUIRE_EQ_MSG(0, fstat(fd, &st),
+ "fstat failed: %s", strerror(errno));
+
+ return (st.st_size);
+}
+
+/*
+ * NB: `nbytes` == 0 has special connotations given the sendfile(2) API
+ * contract. In short, "send the whole file" (paraphrased).
+ */
+static void
+verify_source_and_dest(const char* dest_filename, int src_fd, off_t offset,
+ size_t nbytes)
+{
+ char *dest_pointer, *src_pointer;
+ off_t dest_file_size, src_file_size;
+ size_t length;
+ int dest_fd;
+
+ atf_utils_cat_file(dest_filename, "dest_file: ");
+
+ dest_fd = open(dest_filename, O_RDONLY);
+ ATF_REQUIRE_MSG(dest_fd != -1, "open failed");
+
+ dest_file_size = file_size_from_fd(dest_fd);
+ src_file_size = file_size_from_fd(src_fd);
+
+ /*
+ * Per sendfile(2), "send the whole file" (paraphrased). This means
+ * that we need to grab the file size, as passing in length = 0 with
+ * mmap(2) will result in a failure with EINVAL (length = 0 is invalid).
+ */
+ length = (nbytes == 0) ? (size_t)(src_file_size - offset) : nbytes;
+
+ ATF_REQUIRE_EQ_MSG(dest_file_size, length,
+ "number of bytes written out to %s (%ju) doesn't match the "
+ "expected number of bytes (%zu)", dest_filename, dest_file_size,
+ length);
+
+ ATF_REQUIRE_EQ_MSG(0, lseek(src_fd, offset, SEEK_SET),
+ "lseek failed: %s", strerror(errno));
+
+ dest_pointer = mmap(NULL, length, PROT_READ, MAP_PRIVATE, dest_fd, 0);
+ ATF_REQUIRE_MSG(dest_pointer != MAP_FAILED, "mmap failed: %s",
+ strerror(errno));
+
+ printf("Will mmap in the source file from offset=%jd to length=%zu\n",
+ offset, length);
+
+ src_pointer = mmap(NULL, length, PROT_READ, MAP_PRIVATE, src_fd, offset);
+ ATF_REQUIRE_MSG(src_pointer != MAP_FAILED, "mmap failed: %s",
+ strerror(errno));
+
+ ATF_REQUIRE_EQ_MSG(0, memcmp(src_pointer, dest_pointer, length),
+ "Contents of source and destination do not match. '%s' != '%s'",
+ src_pointer, dest_pointer);
+
+ (void)munmap(src_pointer, length);
+ (void)munmap(dest_pointer, length);
+ (void)close(dest_fd);
+}
+
+static void
+fd_positive_file_test(int domain)
+{
+ off_t offset;
+ size_t nbytes, pattern_size;
+ int client_sock, error, fd, port, server_sock;
+ pid_t server_pid;
+
+ pattern_size = strlen(DETERMINISTIC_PATTERN);
+
+ atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);
+ fd = open(SOURCE_FILE, O_RDONLY);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+
+ port = generate_random_port(__LINE__ + domain);
+ server_sock = setup_tcp_server(domain, port);
+ client_sock = setup_tcp_client(domain, port);
+
+ server_pid = atf_utils_fork();
+ if (server_pid == 0) {
+ (void)close(client_sock);
+ server_cat(DESTINATION_FILE, server_sock, pattern_size);
+ _exit(0);
+ } else
+ (void)close(server_sock);
+
+ nbytes = 0;
+ offset = 0;
+ error = sendfile(fd, client_sock, offset, nbytes, NULL, NULL,
+ SF_FLAGS(0, 0));
+ ATF_REQUIRE_EQ_MSG(0, error, "sendfile failed: %s", strerror(errno));
+ (void)close(client_sock);
+
+ atf_utils_wait(server_pid, 0, "", "");
+ verify_source_and_dest(DESTINATION_FILE, fd, offset, nbytes);
+
+ (void)close(fd);
+}
+
+ATF_TC(fd_positive_file_v4);
+ATF_TC_HEAD(fd_positive_file_v4, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify regular file as file descriptor support (IPv4)");
+}
+ATF_TC_BODY(fd_positive_file_v4, tc)
+{
+
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ fd_positive_file_test(AF_INET);
+}
+
+ATF_TC(fd_positive_file_v6);
+ATF_TC_HEAD(fd_positive_file_v6, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify regular file as file descriptor support (IPv6)");
+}
+ATF_TC_BODY(fd_positive_file_v6, tc)
+{
+
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ fd_positive_file_test(AF_INET6);
+}
+
+static void
+fd_positive_shm_test(int domain)
+{
+ char *shm_pointer;
+ off_t offset;
+ size_t nbytes, pattern_size;
+ pid_t server_pid;
+ int client_sock, error, fd, port, server_sock;
+
+ pattern_size = strlen(DETERMINISTIC_PATTERN);
+
+ printf("pattern size: %zu\n", pattern_size);
+
+ fd = shm_open(SHM_ANON, O_RDWR|O_CREAT, 0600);
+ ATF_REQUIRE_MSG(fd != -1, "shm_open failed: %s", strerror(errno));
+ ATF_REQUIRE_EQ_MSG(0, ftruncate(fd, pattern_size),
+ "ftruncate failed: %s", strerror(errno));
+ shm_pointer = mmap(NULL, pattern_size, PROT_READ|PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ ATF_REQUIRE_MSG(shm_pointer != MAP_FAILED,
+ "mmap failed: %s", strerror(errno));
+ memcpy(shm_pointer, DETERMINISTIC_PATTERN, pattern_size);
+ ATF_REQUIRE_EQ_MSG(0,
+ memcmp(shm_pointer, DETERMINISTIC_PATTERN, pattern_size),
+ "memcmp showed data mismatch: '%s' != '%s'",
+ DETERMINISTIC_PATTERN, shm_pointer);
+
+ port = generate_random_port(__LINE__ + domain);
+ server_sock = setup_tcp_server(domain, port);
+ client_sock = setup_tcp_client(domain, port);
+
+ server_pid = atf_utils_fork();
+ if (server_pid == 0) {
+ (void)close(client_sock);
+ server_cat(DESTINATION_FILE, server_sock, pattern_size);
+ _exit(0);
+ } else
+ (void)close(server_sock);
+
+ nbytes = 0;
+ offset = 0;
+ error = sendfile(fd, client_sock, offset, nbytes, NULL, NULL,
+ SF_FLAGS(0, 0));
+ ATF_REQUIRE_EQ_MSG(0, error, "sendfile failed: %s", strerror(errno));
+ (void)close(client_sock);
+
+ atf_utils_wait(server_pid, 0, "", "");
+ verify_source_and_dest(DESTINATION_FILE, fd, offset, nbytes);
+
+ (void)munmap(shm_pointer, sizeof(DETERMINISTIC_PATTERN));
+ (void)close(fd);
+}
+
+ATF_TC(fd_positive_shm_v4);
+ATF_TC_HEAD(fd_positive_shm_v4, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify shared memory as file descriptor support (IPv4)");
+}
+ATF_TC_BODY(fd_positive_shm_v4, tc)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ fd_positive_shm_test(AF_INET);
+}
+
+ATF_TC(fd_positive_shm_v6);
+ATF_TC_HEAD(fd_positive_shm_v6, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify shared memory as file descriptor support (IPv6))");
+}
+ATF_TC_BODY(fd_positive_shm_v6, tc)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ fd_positive_shm_test(AF_INET6);
+}
+
+static void
+fd_negative_bad_fd_test(int domain)
+{
+ int client_sock, error, fd, port, server_sock;
+
+ port = generate_random_port(__LINE__ + domain);
+ server_sock = setup_tcp_server(domain, port);
+ client_sock = setup_tcp_client(domain, port);
+
+ fd = -1;
+
+ error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));
+ ATF_REQUIRE_ERRNO(EBADF, error == -1);
+
+ (void)close(client_sock);
+ (void)close(server_sock);
+}
+
+ATF_TC(fd_negative_bad_fd_v4);
+ATF_TC_HEAD(fd_negative_bad_fd_v4, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify bad file descriptor returns EBADF (IPv4)");
+}
+ATF_TC_BODY(fd_negative_bad_fd_v4, tc)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ fd_negative_bad_fd_test(AF_INET);
+}
+
+ATF_TC(fd_negative_bad_fd_v6);
+ATF_TC_HEAD(fd_negative_bad_fd_v6, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify bad file descriptor returns EBADF (IPv6)");
+}
+ATF_TC_BODY(fd_negative_bad_fd_v6, tc)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ fd_negative_bad_fd_test(AF_INET6);
+}
+
+static void
+flags_test(int domain)
+{
+ off_t offset;
+ size_t nbytes, pattern_size;
+ int client_sock, error, fd, i, port, server_sock;
+ pid_t server_pid;
+ int16_t number_pages = 10;
+
+ pattern_size = strlen(DETERMINISTIC_PATTERN);
+
+ struct testcase {
+ int16_t readahead_pages, flags;
+ } testcases[] = {
+ /* This is covered in `:fd_positive_file` */
+#if 0
+ {
+ .readahead_pages = 0,
+ .flags = 0
+ },
+#endif
+ {
+ .readahead_pages = 0,
+ .flags = SF_NOCACHE
+ },
+#ifdef SF_USER_READAHEAD
+ {
+ .readahead_pages = 0,
+ .flags = SF_NOCACHE|SF_USER_READAHEAD
+ },
+ {
+ .readahead_pages = 0,
+ .flags = SF_USER_READAHEAD
+ },
+#endif
+ {
+ .readahead_pages = number_pages,
+ .flags = 0
+ },
+ {
+ .readahead_pages = number_pages,
+ .flags = SF_NOCACHE
+ },
+#ifdef SF_USER_READAHEAD
+ {
+ .readahead_pages = number_pages,
+ .flags = SF_NOCACHE|SF_USER_READAHEAD
+ },
+#endif
+ {
+ .readahead_pages = number_pages,
+ .flags = SF_NOCACHE
+ },
+ {
+ .readahead_pages = number_pages,
+ .flags = SF_NODISKIO
+ }
+ };
+
+ atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);
+ for (i = 0; i < nitems(testcases); i++) {
+ fd = open(SOURCE_FILE, O_RDONLY);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+
+ port = generate_random_port(i * __LINE__ + domain);
+ server_sock = setup_tcp_server(domain, port);
+ client_sock = setup_tcp_client(domain, port);
+
+ server_pid = atf_utils_fork();
+ if (server_pid == 0) {
+ (void)close(client_sock);
+ server_cat(DESTINATION_FILE, server_sock, pattern_size);
+ _exit(0);
+ } else
+ (void)close(server_sock);
+
+ nbytes = 0;
+ offset = 0;
+ error = sendfile(fd, client_sock, offset, nbytes, NULL, NULL,
+ SF_FLAGS(testcases[i].readahead_pages, testcases[i].flags));
+ ATF_CHECK_EQ_MSG(error, 0, "sendfile testcase #%d failed: %s",
+ i, strerror(errno));
+ (void)close(client_sock);
+
+ atf_utils_wait(server_pid, 0, "", "");
+ verify_source_and_dest(DESTINATION_FILE, fd, offset, nbytes);
+
+ (void)close(fd);
+ }
+}
+
+ATF_TC(flags_v4);
+ATF_TC_HEAD(flags_v4, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Verify flags functionality (IPv4)");
+}
+ATF_TC_BODY(flags_v4, tc)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ flags_test(AF_INET);
+}
+
+ATF_TC(flags_v6);
+ATF_TC_HEAD(flags_v6, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "Verify flags functionality (IPv6)");
+}
+ATF_TC_BODY(flags_v6, tc)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ flags_test(AF_INET6);
+}
+
+static void
+hdtr_positive_test(int domain)
+{
+ struct iovec headers[1], trailers[1];
+ struct testcase {
+ bool include_headers, include_trailers;
+ } testcases[] = {
+ /* This is covered in `:fd_positive_file` */
+#if 0
+ {
+ .include_headers = false,
+ .include_trailers = false
+ },
+#endif
+ {
+ .include_headers = true,
+ .include_trailers = false
+ },
+ {
+ .include_headers = false,
+ .include_trailers = true
+ },
+ {
+ .include_headers = true,
+ .include_trailers = true
+ }
+ };
+ off_t offset;
+ size_t nbytes;
+ int client_sock, error, fd, fd2, i, port, rc, server_sock;
+ pid_t server_pid;
+
+ headers[0].iov_base = "This is a header";
+ headers[0].iov_len = strlen(headers[0].iov_base);
+ trailers[0].iov_base = "This is a trailer";
+ trailers[0].iov_len = strlen(trailers[0].iov_base);
+ offset = 0;
+ nbytes = 0;
+
+ for (i = 0; i < nitems(testcases); i++) {
+ struct sf_hdtr hdtr;
+ char *pattern;
+
+ if (testcases[i].include_headers) {
+ hdtr.headers = headers;
+ hdtr.hdr_cnt = nitems(headers);
+ } else {
+ hdtr.headers = NULL;
+ hdtr.hdr_cnt = 0;
+ }
+
+ if (testcases[i].include_trailers) {
+ hdtr.trailers = trailers;
+ hdtr.trl_cnt = nitems(trailers);
+ } else {
+ hdtr.trailers = NULL;
+ hdtr.trl_cnt = 0;
+ }
+
+ port = generate_random_port(i * __LINE__ + domain);
+ server_sock = setup_tcp_server(domain, port);
+ client_sock = setup_tcp_client(domain, port);
+
+ rc = asprintf(&pattern, "%s%s%s",
+ testcases[i].include_headers ? (char *)headers[0].iov_base : "",
+ DETERMINISTIC_PATTERN,
+ testcases[i].include_trailers ? (char *)trailers[0].iov_base : "");
+ ATF_REQUIRE_MSG(rc != -1, "asprintf failed: %s", strerror(errno));
+
+ atf_utils_create_file(SOURCE_FILE ".full", "%s", pattern);
+ atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);
+
+ fd = open(SOURCE_FILE, O_RDONLY);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+
+ fd2 = open(SOURCE_FILE ".full", O_RDONLY);
+ ATF_REQUIRE_MSG(fd2 != -1, "open failed: %s", strerror(errno));
+
+ server_pid = atf_utils_fork();
+ if (server_pid == 0) {
+ (void)close(client_sock);
+ server_cat(DESTINATION_FILE, server_sock,
+ strlen(pattern));
+ _exit(0);
+ } else
+ (void)close(server_sock);
+
+ error = sendfile(fd, client_sock, offset, nbytes, &hdtr,
+ NULL, SF_FLAGS(0, 0));
+ ATF_CHECK_EQ_MSG(error, 0, "sendfile testcase #%d failed: %s",
+ i, strerror(errno));
+ (void)close(client_sock);
+
+ atf_utils_wait(server_pid, 0, "", "");
+ verify_source_and_dest(DESTINATION_FILE, fd2, offset, nbytes);
+
+ (void)close(fd);
+ (void)close(fd2);
+ free(pattern);
+ pattern = NULL;
+ }
+}
+
+ATF_TC(hdtr_positive_v4);
+ATF_TC_HEAD(hdtr_positive_v4, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify positive hdtr functionality (IPv4)");
+}
+ATF_TC_BODY(hdtr_positive_v4, tc)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ hdtr_positive_test(AF_INET);
+}
+
+ATF_TC(hdtr_positive_v6);
+ATF_TC_HEAD(hdtr_positive_v6, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify positive hdtr functionality (IPv6)");
+}
+ATF_TC_BODY(hdtr_positive_v6, tc)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ hdtr_positive_test(AF_INET);
+}
+
+static void
+hdtr_negative_bad_pointers_test(int domain)
+{
+ int client_sock, error, fd, port, server_sock;
+ struct sf_hdtr *hdtr1, hdtr2, hdtr3;
+
+ port = generate_random_port(__LINE__ + domain);
+
+ hdtr1 = (struct sf_hdtr*)-1;
+
+ memset(&hdtr2, 0, sizeof(hdtr2));
+ hdtr2.hdr_cnt = 1;
+ hdtr2.headers = (struct iovec*)-1;
+
+ memset(&hdtr3, 0, sizeof(hdtr3));
+ hdtr3.trl_cnt = 1;
+ hdtr3.trailers = (struct iovec*)-1;
+
+ fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+
+ server_sock = setup_tcp_server(domain, port);
+ client_sock = setup_tcp_client(domain, port);
+
+ error = sendfile(fd, client_sock, 0, 0, hdtr1, NULL, SF_FLAGS(0, 0));
+ ATF_CHECK_ERRNO(EFAULT, error == -1);
+
+ error = sendfile(fd, client_sock, 0, 0, &hdtr2, NULL, SF_FLAGS(0, 0));
+ ATF_CHECK_ERRNO(EFAULT, error == -1);
+
+ error = sendfile(fd, client_sock, 0, 0, &hdtr3, NULL, SF_FLAGS(0, 0));
+ ATF_CHECK_ERRNO(EFAULT, error == -1);
+
+ (void)close(fd);
+ (void)close(client_sock);
+ (void)close(server_sock);
+}
+
+ATF_TC(hdtr_negative_bad_pointers_v4);
+ATF_TC_HEAD(hdtr_negative_bad_pointers_v4, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that bad pointers for hdtr storage result in EFAULT (IPv4)");
+}
+ATF_TC_BODY(hdtr_negative_bad_pointers_v4, tc)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ hdtr_negative_bad_pointers_test(AF_INET);
+}
+
+ATF_TC(hdtr_negative_bad_pointers_v6);
+ATF_TC_HEAD(hdtr_negative_bad_pointers_v6, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that bad pointers for hdtr storage result in EFAULT (IPv6)");
+}
+ATF_TC_BODY(hdtr_negative_bad_pointers_v6, tc)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ hdtr_negative_bad_pointers_test(AF_INET6);
+}
+
+static void
+offset_negative_value_less_than_zero_test(int domain)
+{
+ int client_sock, error, fd, port, server_sock;
+
+ port = generate_random_port(__LINE__ + domain);
+ server_sock = setup_tcp_server(domain, port);
+ client_sock = setup_tcp_client(domain, port);
+
+ fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+
+ error = sendfile(fd, client_sock, -1, 0, NULL, NULL, SF_FLAGS(0, 0));
+ ATF_REQUIRE_ERRNO(EINVAL, error == -1);
+
+ (void)close(fd);
+ (void)close(client_sock);
+ (void)close(server_sock);
+}
+
+ATF_TC(offset_negative_value_less_than_zero_v4);
+ATF_TC_HEAD(offset_negative_value_less_than_zero_v4, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that a negative offset results in EINVAL (IPv4)");
+}
+ATF_TC_BODY(offset_negative_value_less_than_zero_v4, tc)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ offset_negative_value_less_than_zero_test(AF_INET);
+}
+
+ATF_TC(offset_negative_value_less_than_zero_v6);
+ATF_TC_HEAD(offset_negative_value_less_than_zero_v6, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that a negative offset results in EINVAL (IPv6)");
+}
+ATF_TC_BODY(offset_negative_value_less_than_zero_v6, tc)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ offset_negative_value_less_than_zero_test(AF_INET6);
+}
+
+static void
+sbytes_positive_test(int domain)
+{
+ size_t pattern_size = strlen(DETERMINISTIC_PATTERN);
+ off_t sbytes;
+ int client_sock, error, fd, port, server_sock;
+
+ port = generate_random_port(__LINE__ + domain);
+ server_sock = setup_tcp_server(domain, port);
+ client_sock = setup_tcp_client(domain, port);
+
+ atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);
+ fd = open(SOURCE_FILE, O_RDONLY);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+
+ error = sendfile(fd, client_sock, 0, 0, NULL, &sbytes, SF_FLAGS(0, 0));
+ ATF_CHECK_EQ_MSG(error, 0, "sendfile failed: %s", strerror(errno));
+
+ (void)close(fd);
+ (void)close(client_sock);
+ (void)close(server_sock);
+
+ ATF_CHECK_EQ_MSG(pattern_size, sbytes,
+ "the value returned by sbytes does not match the expected pattern "
+ "size");
+}
+
+ATF_TC(sbytes_positive_v4);
+ATF_TC_HEAD(sbytes_positive_v4, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify positive `sbytes` functionality (IPv4)");
+}
+ATF_TC_BODY(sbytes_positive_v4, tc)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ sbytes_positive_test(AF_INET);
+}
+
+ATF_TC(sbytes_positive_v6);
+ATF_TC_HEAD(sbytes_positive_v6, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify positive `sbytes` functionality (IPv6)");
+}
+ATF_TC_BODY(sbytes_positive_v6, tc)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ sbytes_positive_test(AF_INET6);
+}
+
+static void
+sbytes_negative_test(int domain)
+{
+ off_t *sbytes_p = (off_t*)-1;
+ int client_sock, error, fd, port, server_sock;
+
+ port = generate_random_port(__LINE__ + domain);
+ server_sock = setup_tcp_server(domain, port);
+ client_sock = setup_tcp_client(domain, port);
+
+ atf_utils_create_file(SOURCE_FILE, "%s", DETERMINISTIC_PATTERN);
+ fd = open(SOURCE_FILE, O_RDONLY);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+
+ atf_tc_expect_fail(
+ "bug 232210: EFAULT assert fails because copyout(9) call is not checked");
+
+ error = sendfile(fd, client_sock, 0, 0, NULL, sbytes_p, SF_FLAGS(0, 0));
+ ATF_REQUIRE_ERRNO(EFAULT, error == -1);
+
+ (void)close(fd);
+ (void)close(client_sock);
+ (void)close(server_sock);
+}
+
+ATF_TC(sbytes_negative_v4);
+ATF_TC_HEAD(sbytes_negative_v4, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify negative `sbytes` functionality (IPv4)");
+}
+ATF_TC_BODY(sbytes_negative_v4, tc)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ sbytes_negative_test(AF_INET);
+}
+
+ATF_TC(sbytes_negative_v6);
+ATF_TC_HEAD(sbytes_negative_v6, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify negative `sbytes` functionality (IPv6)");
+}
+ATF_TC_BODY(sbytes_negative_v6, tc)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ sbytes_negative_test(AF_INET6);
+}
+
+static void
+s_negative_not_connected_socket_test(int domain)
+{
+ int client_sock, error, fd, port;
+
+ port = generate_random_port(__LINE__ + domain);
+ client_sock = setup_tcp_server(domain, port);
+
+ fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+
+ error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));
+ ATF_REQUIRE_ERRNO(ENOTCONN, error == -1);
+
+ (void)close(fd);
+ (void)close(client_sock);
+}
+
+ATF_TC(s_negative_not_connected_socket_v4);
+ATF_TC_HEAD(s_negative_not_connected_socket_v4, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that a non-connected SOCK_STREAM socket results in ENOTCONN (IPv4)");
+}
+
+ATF_TC_BODY(s_negative_not_connected_socket_v4, tc)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ s_negative_not_connected_socket_test(AF_INET);
+}
+
+ATF_TC(s_negative_not_connected_socket_v6);
+ATF_TC_HEAD(s_negative_not_connected_socket_v6, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that a non-connected SOCK_STREAM socket results in ENOTCONN (IPv6)");
+}
+
+ATF_TC_BODY(s_negative_not_connected_socket_v6, tc)
+{
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ s_negative_not_connected_socket_test(AF_INET6);
+}
+
+ATF_TC(s_negative_not_descriptor);
+ATF_TC_HEAD(s_negative_not_descriptor, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that an invalid file descriptor, e.g., -1, fails with EBADF");
+}
+
+ATF_TC_BODY(s_negative_not_descriptor, tc)
+{
+ int client_sock, error, fd;
+
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ client_sock = -1;
+
+ fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+
+ error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));
+ ATF_REQUIRE_ERRNO(EBADF, error == -1);
+
+ (void)close(fd);
+}
+
+ATF_TC(s_negative_not_socket_file_descriptor);
+ATF_TC_HEAD(s_negative_not_socket_file_descriptor, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that a non-socket file descriptor fails with ENOTSOCK");
+}
+
+ATF_TC_BODY(s_negative_not_socket_file_descriptor, tc)
+{
+ int client_sock, error, fd;
+
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+
+ client_sock = open(_PATH_DEVNULL, O_WRONLY);
+ ATF_REQUIRE_MSG(client_sock != -1, "open failed: %s", strerror(errno));
+
+ error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));
+ ATF_REQUIRE_ERRNO(ENOTSOCK, error == -1);
+
+ (void)close(fd);
+ (void)close(client_sock);
+}
+
+static void
+s_negative_udp_socket_test(int domain)
+{
+ int client_sock, error, fd, port;
+
+ port = generate_random_port(__LINE__ + domain);
+ client_sock = setup_client(domain, SOCK_DGRAM, port);
+
+ fd = open(SOURCE_FILE, O_CREAT|O_RDWR, 0600);
+ ATF_REQUIRE_MSG(fd != -1, "open failed: %s", strerror(errno));
+
+ error = sendfile(fd, client_sock, 0, 0, NULL, NULL, SF_FLAGS(0, 0));
+ ATF_REQUIRE_ERRNO(EINVAL, error == -1);
+
+ (void)close(fd);
+ (void)close(client_sock);
+}
+
+ATF_TC(s_negative_udp_socket_v4);
+ATF_TC_HEAD(s_negative_udp_socket_v4, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that a non-SOCK_STREAM type socket results in EINVAL (IPv4)");
+}
+ATF_TC_BODY(s_negative_udp_socket_v4, tc)
+{
+
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ s_negative_udp_socket_test(AF_INET);
+}
+
+ATF_TC(s_negative_udp_socket_v6);
+ATF_TC_HEAD(s_negative_udp_socket_v6, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr",
+ "Verify that a non-SOCK_STREAM type socket results in EINVAL (IPv6)");
+}
+ATF_TC_BODY(s_negative_udp_socket_v6, tc)
+{
+
+ if (atf_tc_get_config_var_as_bool_wd(tc, "qemu", false))
+ atf_tc_skip("Sendfile(4) unimplemented. https://github.com/qemu-bsd-user/qemu-bsd-user/issues/25");
+
+ s_negative_udp_socket_test(AF_INET6);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, fd_positive_file_v4);
+ ATF_TP_ADD_TC(tp, fd_positive_file_v6);
+ ATF_TP_ADD_TC(tp, fd_positive_shm_v4);
+ ATF_TP_ADD_TC(tp, fd_positive_shm_v6);
+ ATF_TP_ADD_TC(tp, fd_negative_bad_fd_v4);
+ ATF_TP_ADD_TC(tp, fd_negative_bad_fd_v6);
+ ATF_TP_ADD_TC(tp, flags_v4);
+ ATF_TP_ADD_TC(tp, flags_v6);
+ /*
+ * TODO: the negative case for SF_NODISKIO (returns EBUSY if file in
+ * use) is not covered yet.
+ *
+ * Need to lock a file in a subprocess in write mode, then try and
+ * send the data in read mode with sendfile.
+ *
+ * This should work with FFS/UFS, but there are no guarantees about
+ * other filesystem implementations of sendfile(2), e.g., ZFS.
+ */
+ ATF_TP_ADD_TC(tp, hdtr_positive_v4);
+ ATF_TP_ADD_TC(tp, hdtr_positive_v6);
+ ATF_TP_ADD_TC(tp, hdtr_negative_bad_pointers_v4);
+ ATF_TP_ADD_TC(tp, hdtr_negative_bad_pointers_v6);
+ ATF_TP_ADD_TC(tp, offset_negative_value_less_than_zero_v4);
+ ATF_TP_ADD_TC(tp, offset_negative_value_less_than_zero_v6);
+ ATF_TP_ADD_TC(tp, sbytes_positive_v4);
+ ATF_TP_ADD_TC(tp, sbytes_positive_v6);
+ ATF_TP_ADD_TC(tp, sbytes_negative_v4);
+ ATF_TP_ADD_TC(tp, sbytes_negative_v6);
+ ATF_TP_ADD_TC(tp, s_negative_not_connected_socket_v4);
+ ATF_TP_ADD_TC(tp, s_negative_not_connected_socket_v6);
+ ATF_TP_ADD_TC(tp, s_negative_not_descriptor);
+ ATF_TP_ADD_TC(tp, s_negative_not_socket_file_descriptor);
+ ATF_TP_ADD_TC(tp, s_negative_udp_socket_v4);
+ ATF_TP_ADD_TC(tp, s_negative_udp_socket_v6);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/tests/sys/swapcontext_test.c b/lib/libc/tests/sys/swapcontext_test.c
new file mode 100644
index 000000000000..f341a746e515
--- /dev/null
+++ b/lib/libc/tests/sys/swapcontext_test.c
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 2025 Raptor Computing Systems, LLC
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ucontext.h>
+#include <errno.h>
+
+#include <atf-c.h>
+
+#define STACK_SIZE (64ull << 10)
+
+static volatile int callback_reached = 0;
+
+static ucontext_t uctx_save, uctx_switch;
+
+static void swapcontext_callback()
+{
+ // Increment callback reached variable
+ // If this is called multiple times, we will fail the test
+ // If this is not called at all, we will fail the test
+ callback_reached++;
+}
+
+ATF_TC(swapcontext_basic);
+ATF_TC_HEAD(swapcontext_basic, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify basic functionality of swapcontext");
+}
+
+ATF_TC_BODY(swapcontext_basic, tc)
+{
+ char *stack;
+ int res;
+
+ stack = malloc(STACK_SIZE);
+ ATF_REQUIRE_MSG(stack != NULL, "malloc failed: %s", strerror(errno));
+ res = getcontext(&uctx_switch);
+ ATF_REQUIRE_MSG(res == 0, "getcontext failed: %s", strerror(errno));
+
+ uctx_switch.uc_stack.ss_sp = stack;
+ uctx_switch.uc_stack.ss_size = STACK_SIZE;
+ uctx_switch.uc_link = &uctx_save;
+ makecontext(&uctx_switch, swapcontext_callback, 0);
+
+ res = swapcontext(&uctx_save, &uctx_switch);
+
+ ATF_REQUIRE_MSG(res == 0, "swapcontext failed: %s", strerror(errno));
+ ATF_REQUIRE_MSG(callback_reached == 1,
+ "callback failed, reached %d times", callback_reached);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, swapcontext_basic);
+
+ return (atf_no_error());
+}
+
diff --git a/lib/libc/tests/termios/Makefile b/lib/libc/tests/termios/Makefile
new file mode 100644
index 000000000000..76879b805035
--- /dev/null
+++ b/lib/libc/tests/termios/Makefile
@@ -0,0 +1,7 @@
+.include <bsd.own.mk>
+
+NETBSD_ATF_TESTS_C= tcsetpgrp_test
+
+.include "../Makefile.netbsd-tests"
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/termios/Makefile.depend b/lib/libc/tests/termios/Makefile.depend
new file mode 100644
index 000000000000..e89a5c52c82a
--- /dev/null
+++ b/lib/libc/tests/termios/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libnetbsd \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/time/Makefile b/lib/libc/tests/time/Makefile
new file mode 100644
index 000000000000..9e0230a70f54
--- /dev/null
+++ b/lib/libc/tests/time/Makefile
@@ -0,0 +1,8 @@
+.include <bsd.own.mk>
+
+NETBSD_ATF_TESTS_C= mktime_test
+NETBSD_ATF_TESTS_C+= strptime_test
+
+.include "../Makefile.netbsd-tests"
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/time/Makefile.depend b/lib/libc/tests/time/Makefile.depend
new file mode 100644
index 000000000000..e89a5c52c82a
--- /dev/null
+++ b/lib/libc/tests/time/Makefile.depend
@@ -0,0 +1,18 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libnetbsd \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/tls/Makefile b/lib/libc/tests/tls/Makefile
new file mode 100644
index 000000000000..6d53493f56f5
--- /dev/null
+++ b/lib/libc/tests/tls/Makefile
@@ -0,0 +1,31 @@
+.include <bsd.own.mk>
+
+.if !defined(NO_PIC)
+SUBDIR+= dso
+.endif
+
+# TODO: doesn't link properly (for some odd reason it's trying to link in
+# libatf.so)
+#NETBSD_ATF_TESTS_C= tls_static_test
+.if !defined(NO_PIC)
+NETBSD_ATF_TESTS_C+= tls_dlopen_test
+NETBSD_ATF_TESTS_C+= tls_dynamic_test
+.endif
+
+.include "../Makefile.netbsd-tests"
+
+DSODIR= ${.OBJDIR:H}/tls_dso
+
+LIBADD.tls_static_test+= pthread
+LDFLAGS.tls_static_test+= -static
+SRCS.tls_static_test= t_tls_static.c t_tls_static_helper.c
+
+DPADD.tls_dynamic_test+= ${DSODIR}/libh_tls_dynamic.so
+LDADD.tls_dynamic_test+= -lh_tls_dynamic
+LDFLAGS.tls_dynamic_test+= -Wl,-rpath,${TESTSDIR} -L${DSODIR}
+LIBADD.tls_dynamic_test+= pthread
+
+LIBADD.tls_dlopen_test+= pthread
+LDFLAGS.tls_dlopen_test+= -Wl,-rpath,${TESTSDIR} -Wl,-export-dynamic
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/tls/Makefile.depend b/lib/libc/tests/tls/Makefile.depend
new file mode 100644
index 000000000000..e53b1a169e02
--- /dev/null
+++ b/lib/libc/tests/tls/Makefile.depend
@@ -0,0 +1,20 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libc/tests/tls_dso \
+ lib/libcompiler_rt \
+ lib/libnetbsd \
+ lib/libthr \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/tls/dso/Makefile b/lib/libc/tests/tls/dso/Makefile
new file mode 100644
index 000000000000..783534ff7aae
--- /dev/null
+++ b/lib/libc/tests/tls/dso/Makefile
@@ -0,0 +1,16 @@
+TESTSRC= ${SRCTOP}/contrib/netbsd-tests/lib/libc/tls/${.CURDIR:T}
+
+LIB= h_tls_dlopen
+SHLIB_NAME= h_tls_dlopen.so
+SRCS= h_tls_dlopen.c
+
+MAN=
+PACKAGE= tests
+NO_DEV_PACKAGE=
+
+LIBDIR= ${TESTSBASE}/lib/libc/tls
+SHLIB_MAJOR= 1
+
+.include "../../Makefile.netbsd-tests"
+
+.include <bsd.lib.mk>
diff --git a/lib/libc/tests/tls/dso/Makefile.depend b/lib/libc/tests/tls/dso/Makefile.depend
new file mode 100644
index 000000000000..1af0c88e099c
--- /dev/null
+++ b/lib/libc/tests/tls/dso/Makefile.depend
@@ -0,0 +1,17 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/tls_dso/Makefile b/lib/libc/tests/tls_dso/Makefile
new file mode 100644
index 000000000000..7cb8f98b431e
--- /dev/null
+++ b/lib/libc/tests/tls_dso/Makefile
@@ -0,0 +1,20 @@
+.include <bsd.own.mk>
+
+LIB= h_tls_dynamic
+SRCS= h_tls_dynamic.c
+
+LIBDIR= ${TESTSBASE}/lib/libc/tls
+SHLIBDIR= ${TESTSBASE}/lib/libc/tls
+SHLIB_MAJOR= 1
+PACKAGE= tests
+NO_DEV_PACKAGE=
+
+WITHOUT_STATIC=
+WITHOUT_PROFILE=
+WITHOUT_PIC=
+
+MAN=
+
+.include "../Makefile.netbsd-tests"
+
+.include <bsd.lib.mk>
diff --git a/lib/libc/tests/tls_dso/Makefile.depend b/lib/libc/tests/tls_dso/Makefile.depend
new file mode 100644
index 000000000000..93249906da4f
--- /dev/null
+++ b/lib/libc/tests/tls_dso/Makefile.depend
@@ -0,0 +1,14 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ include \
+ lib/${CSU_DIR} \
+ lib/libc \
+ lib/libcompiler_rt \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif
diff --git a/lib/libc/tests/ttyio/Makefile b/lib/libc/tests/ttyio/Makefile
new file mode 100644
index 000000000000..2d2dda2f854f
--- /dev/null
+++ b/lib/libc/tests/ttyio/Makefile
@@ -0,0 +1,10 @@
+.include <bsd.own.mk>
+
+# TODO: ptm_test
+NETBSD_ATF_TESTS_C= ttyio_test
+
+LIBADD.ttyio_test+= util
+
+.include "../Makefile.netbsd-tests"
+
+.include <bsd.test.mk>
diff --git a/lib/libc/tests/ttyio/Makefile.depend b/lib/libc/tests/ttyio/Makefile.depend
new file mode 100644
index 000000000000..9df74fa6efd2
--- /dev/null
+++ b/lib/libc/tests/ttyio/Makefile.depend
@@ -0,0 +1,19 @@
+# Autogenerated - do NOT edit!
+
+DIRDEPS = \
+ gnu/lib/csu \
+ include \
+ include/xlocale \
+ lib/${CSU_DIR} \
+ lib/atf/libatf-c \
+ lib/libc \
+ lib/libcompiler_rt \
+ lib/libnetbsd \
+ lib/libutil \
+
+
+.include <dirdeps.mk>
+
+.if ${DEP_RELDIR} == ${_DEP_RELDIR}
+# local dependencies - needed for -jN in clean tree
+.endif