aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compat/Makefile7
-rw-r--r--compat/linux/Makefile61
-rw-r--r--compat/linux/arch/x86_64/h_syscall.c55
-rw-r--r--compat/linux/h_common.sh34
-rw-r--r--compat/linux/h_inotify_directory.c108
-rw-r--r--compat/linux/h_inotify_init.c65
-rw-r--r--compat/linux/h_inotify_single_file.c87
-rw-r--r--compat/linux/h_inotify_watch_change.c88
-rw-r--r--compat/linux/h_linux.c36
-rw-r--r--compat/linux/h_linux.h76
-rw-r--r--compat/linux/h_not_supported.sh33
-rw-r--r--compat/linux/t_inotify.sh79
-rw-r--r--crypto/libcrypto/t_sha512trunc.c170
-rw-r--r--fs/cd9660/h_hexdump_r.c100
-rw-r--r--fs/cd9660/pr_48787.image.hex278
-rw-r--r--fs/lfs/t_basic.c79
-rw-r--r--fs/lfs/t_fcntl.c407
-rw-r--r--fs/lfs/t_orphan.c203
-rw-r--r--fs/lfs/t_resize.c180
-rw-r--r--fs/lfs/util.c181
-rw-r--r--fs/lfs/util.h28
-rw-r--r--games/t_morse.sh127
-rw-r--r--include/t_stddef.c127
-rw-r--r--kernel/arch/aarch64/contextspfunc.S46
-rw-r--r--kernel/arch/aarch64/execregs.c226
-rw-r--r--kernel/arch/aarch64/execregs.h86
-rw-r--r--kernel/arch/aarch64/execsp.S99
-rw-r--r--kernel/arch/aarch64/h_execregs.S84
-rw-r--r--kernel/arch/aarch64/signalsphandler.S46
-rw-r--r--kernel/arch/aarch64/stack_pointer.h42
-rw-r--r--kernel/arch/aarch64/threadspfunc.S43
-rw-r--r--kernel/arch/alpha/contextspfunc.S50
-rw-r--r--kernel/arch/alpha/execsp.S106
-rw-r--r--kernel/arch/alpha/signalsphandler.S49
-rw-r--r--kernel/arch/alpha/stack_pointer.h35
-rw-r--r--kernel/arch/alpha/threadspfunc.S46
-rw-r--r--kernel/arch/arm/contextspfunc.S51
-rw-r--r--kernel/arch/arm/execsp.S119
-rw-r--r--kernel/arch/arm/signalsphandler.S51
-rw-r--r--kernel/arch/arm/stack_pointer.h35
-rw-r--r--kernel/arch/arm/threadspfunc.S43
-rw-r--r--kernel/arch/hppa/contextspfunc.S48
-rw-r--r--kernel/arch/hppa/execregs.c387
-rw-r--r--kernel/arch/hppa/execregs.h157
-rw-r--r--kernel/arch/hppa/execsp.S130
-rw-r--r--kernel/arch/hppa/h_execregs.S168
-rw-r--r--kernel/arch/hppa/signalsphandler.S46
-rw-r--r--kernel/arch/hppa/stack_pointer.h35
-rw-r--r--kernel/arch/hppa/threadspfunc.S43
-rw-r--r--kernel/arch/i386/contextspfunc.S56
-rw-r--r--kernel/arch/i386/execregs.c132
-rw-r--r--kernel/arch/i386/execregs.h69
-rw-r--r--kernel/arch/i386/execsp.S112
-rw-r--r--kernel/arch/i386/h_execregs.S85
-rw-r--r--kernel/arch/i386/signalsphandler.S56
-rw-r--r--kernel/arch/i386/stack_pointer.h35
-rw-r--r--kernel/arch/i386/threadspfunc.S43
-rw-r--r--kernel/arch/mips/contextspfunc.S53
-rw-r--r--kernel/arch/mips/execsp.S118
-rw-r--r--kernel/arch/mips/signalsphandler.S53
-rw-r--r--kernel/arch/mips/stack_pointer.h40
-rw-r--r--kernel/arch/mips/threadspfunc.S46
-rw-r--r--kernel/arch/riscv/contextspfunc.S45
-rw-r--r--kernel/arch/riscv/execsp.S100
-rw-r--r--kernel/arch/riscv/signalsphandler.S45
-rw-r--r--kernel/arch/riscv/stack_pointer.h35
-rw-r--r--kernel/arch/riscv/threadspfunc.S43
-rw-r--r--kernel/arch/sh3/asm.h58
-rw-r--r--kernel/arch/sh3/contextspfunc.S55
-rw-r--r--kernel/arch/sh3/execsp.S146
-rw-r--r--kernel/arch/sh3/h_execregs.S86
-rw-r--r--kernel/arch/sh3/signalsphandler.S61
-rw-r--r--kernel/arch/sh3/stack_pointer.h35
-rw-r--r--kernel/arch/sh3/threadspfunc.S41
-rw-r--r--kernel/arch/sparc/contextspfunc.S64
-rw-r--r--kernel/arch/sparc/execsp.S123
-rw-r--r--kernel/arch/sparc/signalsphandler.S62
-rw-r--r--kernel/arch/sparc/stack_pointer.h35
-rw-r--r--kernel/arch/sparc/threadspfunc.S45
-rw-r--r--kernel/arch/vax/execregs.c166
-rw-r--r--kernel/arch/vax/execregs.h77
-rw-r--r--kernel/arch/vax/h_execregs.S87
-rw-r--r--kernel/arch/x86_64/contextspfunc.S55
-rw-r--r--kernel/arch/x86_64/execregs.c156
-rw-r--r--kernel/arch/x86_64/execregs.h81
-rw-r--r--kernel/arch/x86_64/execsp.S111
-rw-r--r--kernel/arch/x86_64/h_execregs.S82
-rw-r--r--kernel/arch/x86_64/signalsphandler.S55
-rw-r--r--kernel/arch/x86_64/stack_pointer.h35
-rw-r--r--kernel/arch/x86_64/threadspfunc.S54
-rw-r--r--kernel/h_cloexec.c48
-rw-r--r--kernel/h_execregs_unimpl.c37
-rw-r--r--kernel/h_execsp.c68
-rw-r--r--kernel/h_execsp.h47
-rw-r--r--kernel/setjmp_tester/Makefile12
-rw-r--r--kernel/setjmp_tester/setjmp_tester.c147
-rw-r--r--kernel/t_cloexec.c457
-rw-r--r--kernel/t_clofork.c445
-rw-r--r--kernel/t_execregs.c197
-rw-r--r--kernel/t_fdrestart.c274
-rw-r--r--kernel/t_nanosleep.c246
-rw-r--r--kernel/t_semtimedop.c279
-rw-r--r--kernel/t_setjmp.sh63
-rw-r--r--kernel/t_signal_and_sp.c628
-rw-r--r--kernel/t_time_arith.c1224
-rw-r--r--kernel/t_unmount.c106
-rw-r--r--lib/csu/h_hello.c48
-rw-r--r--lib/csu/h_preinit_array.c16
-rw-r--r--lib/csu/t_hello.sh150
-rw-r--r--lib/libc/gen/Makefile.inc1
-rw-r--r--lib/libc/gen/h_ctype_abuse.c138
-rw-r--r--lib/libc/gen/h_execsig.c55
-rw-r--r--lib/libc/gen/t_arc4random.c670
-rw-r--r--lib/libc/gen/t_ctype.c1236
-rw-r--r--lib/libc/gen/t_timespec_get.c123
-rw-r--r--lib/libc/locale/t_c16rtomb.c287
-rw-r--r--lib/libc/locale/t_c32rtomb.c60
-rw-r--r--lib/libc/locale/t_c8rtomb.c355
-rw-r--r--lib/libc/locale/t_mbrtoc16.c364
-rw-r--r--lib/libc/locale/t_mbrtoc32.c61
-rw-r--r--lib/libc/locale/t_mbrtoc8.c415
-rw-r--r--lib/libc/locale/t_uchar.c81
-rw-r--r--lib/libc/misc/t_vis.c165
-rw-r--r--lib/libc/regex/t_regex_binary.c80
-rw-r--r--lib/libc/setjmp/t_sigstack.c389
-rw-r--r--lib/libc/ssp/h_getcwd2.c18
-rw-r--r--lib/libc/stdlib/h_sort.c225
-rw-r--r--lib/libc/stdlib/t_sort.sh115
-rw-r--r--lib/libc/sys/t_aio_cancel.c223
-rw-r--r--lib/libc/sys/t_aio_lio.c264
-rw-r--r--lib/libc/sys/t_aio_rw.c167
-rw-r--r--lib/libc/sys/t_aio_suspend.c170
-rw-r--r--lib/libc/sys/t_ptrace_kill.c131
-rw-r--r--lib/libexecinfo/t_backtrace_sandbox.c88
-rw-r--r--lib/libm/t_errhandling.c97
-rw-r--r--lib/libm/t_next.c887
-rw-r--r--lib/libm/t_remquo.c126
-rw-r--r--lib/libpthread/cancelpoint.h133
-rw-r--r--lib/libpthread/t_cancellation.c1543
-rw-r--r--lib/libpthread/t_compat_cancel.c287
-rw-r--r--lib/libpthread/t_stack.c491
-rw-r--r--lib/libpthread/weak/Makefile25
-rw-r--r--lib/libpthread/weak/Makefile.inc1
-rw-r--r--lib/libpthread/weak/lib/Makefile21
-rw-r--r--lib/libpthread/weak/lib/h_pthread_weak.c83
-rw-r--r--lib/libpthread/weak/lib/h_pthread_weak.h36
-rw-r--r--lib/libpthread/weak/t_pthread_weak_nothread.c64
-rw-r--r--lib/libpthread/weak/t_pthread_weak_threaded.c64
-rw-r--r--lib/libstdc++/Makefile12
-rw-r--r--lib/libstdc++/h_cin_nosync.cc40
-rw-r--r--lib/libstdc++/t_sync_with_stdio.sh41
-rw-r--r--lib/libutil/t_strpct.c202
-rw-r--r--libexec/ld.elf_so/h_r_rel.c147
-rw-r--r--libexec/ld.elf_so/t_dladdr.c171
-rw-r--r--libexec/ld.elf_so/t_dlclose_thread.c98
-rw-r--r--libexec/ld.elf_so/t_r_rel.sh113
-rw-r--r--libexec/ld.elf_so/t_tls_alignment.c70
-rw-r--r--net/if_bridge/broadcast.pcap.uue5
-rw-r--r--net/if_bridge/unicast.pcap.uue5
-rw-r--r--net/if_shmif/Makefile13
-rw-r--r--net/if_shmif/t_shmif.sh138
-rw-r--r--sbin/gpt/gpt.2part.show.label-p7
-rw-r--r--sbin/gpt/gpt.2part.show.normal-p7
-rw-r--r--sbin/gpt/gpt.2part.show.uuid-p7
-rw-r--r--sbin/gpt/gpt.disklabel.show.normal-p11
-rw-r--r--sbin/gpt/gpt.empty.show.clean4
-rw-r--r--sbin/gpt/gpt.empty.show.clean-p3
-rw-r--r--sbin/gpt/gpt.empty.show.normal-p6
-rw-r--r--sbin/gpt/gpt.removepart.show.normal-p7
-rw-r--r--sbin/gpt/gpt.resizedisk.show.normal-p8
-rw-r--r--sbin/gpt/gpt.resizepart.show.normal-p7
-rw-r--r--usr.bin/cc/t_ctype_abuse.sh124
-rw-r--r--usr.bin/cc/t_libm_cabs.sh57
-rw-r--r--usr.bin/cc/t_pthread_abuse.sh79
-rw-r--r--usr.bin/diff/functionname.in29
-rw-r--r--usr.bin/diff/functionname_c.in29
-rw-r--r--usr.bin/diff/functionname_c.out11
-rw-r--r--usr.bin/diff/header.out4
-rw-r--r--usr.bin/diff/header_ns.out4
-rw-r--r--usr.bin/diff/input1.in2
-rw-r--r--usr.bin/diff/input2.in3
-rw-r--r--usr.bin/diff/input_c1.in15
-rw-r--r--usr.bin/diff/input_c2.in16
-rw-r--r--usr.bin/diff/simple.out5
-rw-r--r--usr.bin/diff/simple_b.out6
-rw-r--r--usr.bin/diff/simple_e.out4
-rw-r--r--usr.bin/diff/simple_i.out6
-rw-r--r--usr.bin/diff/simple_n.out4
-rw-r--r--usr.bin/diff/simple_p.out34
-rw-r--r--usr.bin/diff/simple_u.out7
-rw-r--r--usr.bin/diff/simple_w.out6
-rw-r--r--usr.bin/diff/unified_9999.out21
-rw-r--r--usr.bin/diff/unified_p.out20
-rw-r--r--usr.bin/error/Makefile6
-rw-r--r--usr.bin/error/t_error.sh101
-rw-r--r--usr.bin/ftp/Makefile15
-rw-r--r--usr.bin/ftp/custom_headers.sh36
-rw-r--r--usr.bin/ftp/t_custom_headers.sh67
-rw-r--r--usr.bin/gcov/Makefile7
-rw-r--r--usr.bin/gcov/t_gcov.sh104
-rw-r--r--usr.bin/mtree/Makefile8
-rw-r--r--usr.bin/mtree/t_sets.sh126
-rw-r--r--usr.bin/netpgpkeys/Makefile13
-rw-r--r--usr.bin/netpgpkeys/data/testkey-ec.pubbin0 -> 418 bytes
-rw-r--r--usr.bin/netpgpkeys/data/testkey-rsa.pubbin0 -> 2245 bytes
-rw-r--r--usr.bin/netpgpkeys/t_netpgpkeys.sh81
-rw-r--r--usr.bin/netpgpverify/data/D5B22A28.pubbin0 -> 1178 bytes
-rw-r--r--usr.bin/netpgpverify/data/D5B22A28.secretbin0 -> 2480 bytes
-rw-r--r--usr.bin/netpgpverify/data/NetBSD-6.0_hashes.asc.gzbin0 -> 307190 bytes
-rw-r--r--usr.bin/netpgpverify/data/a.gpgbin0 -> 460 bytes
-rw-r--r--usr.bin/netpgpverify/data/b.gpgbin0 -> 449 bytes
-rw-r--r--usr.bin/netpgpverify/data/det16
-rw-r--r--usr.bin/netpgpverify/data/det.sigbin0 -> 287 bytes
-rw-r--r--usr.bin/netpgpverify/data/dsa-pubring.gpgbin0 -> 1647 bytes
-rw-r--r--usr.bin/netpgpverify/data/expected168
-rw-r--r--usr.bin/netpgpverify/data/expected1710
-rw-r--r--usr.bin/netpgpverify/data/expected188
-rw-r--r--usr.bin/netpgpverify/data/expected197
-rw-r--r--usr.bin/netpgpverify/data/expected2018
-rw-r--r--usr.bin/netpgpverify/data/expected218
-rw-r--r--usr.bin/netpgpverify/data/expected228
-rw-r--r--usr.bin/netpgpverify/data/expected237
-rw-r--r--usr.bin/netpgpverify/data/expected248
-rw-r--r--usr.bin/netpgpverify/data/expected257
-rw-r--r--usr.bin/netpgpverify/data/expected267
-rw-r--r--usr.bin/netpgpverify/data/expected275
-rw-r--r--usr.bin/netpgpverify/data/expected285
-rw-r--r--usr.bin/netpgpverify/data/expected297
-rw-r--r--usr.bin/netpgpverify/data/expected307
-rw-r--r--usr.bin/netpgpverify/data/expected3133
-rw-r--r--usr.bin/netpgpverify/data/expected3224
-rw-r--r--usr.bin/netpgpverify/data/expected3340
-rw-r--r--usr.bin/netpgpverify/data/expected348
-rw-r--r--usr.bin/netpgpverify/data/expected3516
-rw-r--r--usr.bin/netpgpverify/data/expected365
-rw-r--r--usr.bin/netpgpverify/data/expected375
-rw-r--r--usr.bin/netpgpverify/data/expected385
-rw-r--r--usr.bin/netpgpverify/data/expected395
-rw-r--r--usr.bin/netpgpverify/data/expected406
-rw-r--r--usr.bin/netpgpverify/data/expected416
-rw-r--r--usr.bin/netpgpverify/data/expected426
-rw-r--r--usr.bin/netpgpverify/data/expected436
-rw-r--r--usr.bin/netpgpverify/data/expected445
-rw-r--r--usr.bin/netpgpverify/data/expected455
-rw-r--r--usr.bin/netpgpverify/data/expected468
-rw-r--r--usr.bin/netpgpverify/data/in1.asc16
-rw-r--r--usr.bin/netpgpverify/data/in1.gpgbin0 -> 235 bytes
-rw-r--r--usr.bin/netpgpverify/data/in2.asc16
-rw-r--r--usr.bin/netpgpverify/data/in2.gpgbin0 -> 236 bytes
-rw-r--r--usr.bin/netpgpverify/data/jj.asc32
-rw-r--r--usr.bin/netpgpverify/data/message1
-rw-r--r--usr.bin/netpgpverify/data/message.keyringbin0 -> 3534 bytes
-rw-r--r--usr.bin/netpgpverify/data/message.v1.asc14
-rw-r--r--usr.bin/netpgpverify/data/message.v1.asc.expected5
-rw-r--r--usr.bin/netpgpverify/data/message.v1.sigbin0 -> 287 bytes
-rw-r--r--usr.bin/netpgpverify/data/message.v1.sig.expected5
-rw-r--r--usr.bin/netpgpverify/data/message.v2.asc15
-rw-r--r--usr.bin/netpgpverify/data/message.v2.asc.expected5
-rw-r--r--usr.bin/netpgpverify/data/message.v2.sigbin0 -> 310 bytes
-rw-r--r--usr.bin/netpgpverify/data/message.v2.sig.expected5
-rw-r--r--usr.bin/netpgpverify/data/pubring.gpgbin0 -> 19264 bytes
-rw-r--r--usr.bin/shmif_pcapin/Makefile11
-rw-r--r--usr.bin/shmif_pcapin/d_pcap.in.bz2.uue27
-rw-r--r--usr.bin/shmif_pcapin/d_pcap.out.bz2.uue13
-rw-r--r--usr.bin/shmif_pcapin/t_basic.sh89
-rw-r--r--usr.bin/stat/Makefile7
-rw-r--r--usr.bin/stat/t_stat.sh68
-rw-r--r--usr.bin/xlint/lint1/expr.c33
-rw-r--r--usr.bin/xlint/lint1/gcc.c130
-rw-r--r--usr.bin/xlint/lint1/init_c99.c739
-rw-r--r--usr.bin/xlint/lint1/lex_utf8.c12
-rw-r--r--usr.bin/xlint/lint1/msg_079_nongcc.c26
-rw-r--r--usr.bin/xlint/lint1/msg_118_ilp32.c47
-rw-r--r--usr.bin/xlint/lint1/msg_356.c35
-rw-r--r--usr.bin/xlint/lint1/msg_357.c57
-rw-r--r--usr.bin/xlint/lint1/msg_358.c65
-rw-r--r--usr.bin/xlint/lint1/msg_359.c29
-rw-r--r--usr.bin/xlint/lint1/msg_360.c28
-rw-r--r--usr.bin/xlint/lint1/msg_361.c52
-rw-r--r--usr.bin/xlint/lint1/msg_362.c33
-rw-r--r--usr.bin/xlint/lint1/msg_363.c64
-rw-r--r--usr.bin/xlint/lint1/msg_364.c35
-rw-r--r--usr.bin/xlint/lint1/msg_365.c34
-rw-r--r--usr.bin/xlint/lint1/msg_366.c70
-rw-r--r--usr.bin/xlint/lint1/msg_367.c120
-rw-r--r--usr.bin/xlint/lint1/msg_368.c34
-rw-r--r--usr.bin/xlint/lint1/msg_369.c52
-rw-r--r--usr.bin/xlint/lint1/msg_370.c52
-rw-r--r--usr.bin/xlint/lint1/msg_371.c62
-rw-r--r--usr.bin/xlint/lint1/msg_372.c36
-rw-r--r--usr.bin/xlint/lint1/msg_373.c44
-rw-r--r--usr.bin/xlint/lint1/msg_374.c49
-rw-r--r--usr.bin/xlint/lint1/msg_375.c38
-rw-r--r--usr.bin/xlint/lint1/msg_376.c58
-rw-r--r--usr.bin/xlint/lint1/msg_377.c41
-rw-r--r--usr.bin/xlint/lint1/msg_378.c52
-rw-r--r--usr.bin/xlint/lint1/msg_379.c31
-rw-r--r--usr.bin/xlint/lint1/msg_380.c71
-rw-r--r--usr.bin/xlint/lint1/msg_381.c71
-rw-r--r--usr.bin/xlint/lint1/msg_382.c42
-rw-r--r--usr.bin/xlint/lint1/msg_383.c56
-rw-r--r--usr.bin/xlint/lint1/msg_384.c26
-rw-r--r--usr.bin/xlint/lint1/msg_385.c58
-rw-r--r--usr.bin/xlint/lint1/msg_386.c34
-rw-r--r--usr.bin/xlint/lint1/platform_ilp32_c90.c243
-rw-r--r--usr.bin/xlint/lint1/platform_ilp32_c99.c227
-rw-r--r--usr.bin/xlint/lint1/platform_ilp32_trad.c130
-rw-r--r--usr.bin/xlint/lint1/platform_lp64_c90.c227
-rw-r--r--usr.bin/xlint/lint1/platform_lp64_c99.c227
-rw-r--r--usr.bin/xlint/lint1/platform_lp64_trad.c122
-rw-r--r--usr.sbin/certctl/Makefile10
-rw-r--r--usr.sbin/certctl/Makefile.inc6
-rw-r--r--usr.sbin/certctl/certs1/DigiCert_Global_Root_CA.pem22
-rw-r--r--usr.sbin/certctl/certs1/Explicitly_Distrust_DigiNotar_Root_CA.pem32
-rw-r--r--usr.sbin/certctl/certs1/Makefile10
-rw-r--r--usr.sbin/certctl/certs2/GTS_Root_R1.pem31
-rw-r--r--usr.sbin/certctl/certs2/GlobalSign_Root_CA_-_R3.pem21
-rw-r--r--usr.sbin/certctl/certs2/Makefile10
-rw-r--r--usr.sbin/certctl/certs3/Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.1.pem35
-rw-r--r--usr.sbin/certctl/certs3/Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.pem35
-rw-r--r--usr.sbin/certctl/certs3/Makefile10
-rw-r--r--usr.sbin/certctl/certs4/AC_RAIZ_FNMT-RCM.pem32
-rw-r--r--usr.sbin/certctl/certs4/DigiCert_Global_Root_CA.pem22
-rw-r--r--usr.sbin/certctl/certs4/Makefile10
-rw-r--r--usr.sbin/certctl/t_certctl.sh486
-rw-r--r--usr.sbin/makefs/Makefile8
-rw-r--r--usr.sbin/makefs/t_makefs.sh65
327 files changed, 29404 insertions, 0 deletions
diff --git a/compat/Makefile b/compat/Makefile
new file mode 100644
index 000000000000..8bd2100560a4
--- /dev/null
+++ b/compat/Makefile
@@ -0,0 +1,7 @@
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/compat
+
+TESTS_SUBDIRS= linux
+
+.include <bsd.test.mk>
diff --git a/compat/linux/Makefile b/compat/linux/Makefile
new file mode 100644
index 000000000000..cd3b1a91bf21
--- /dev/null
+++ b/compat/linux/Makefile
@@ -0,0 +1,61 @@
+# $NetBSD: Makefile,v 1.1 2023/08/19 22:56:44 christos Exp $
+
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/compat/linux
+TESTS_SH= t_inotify
+
+TC_PROGS= h_inotify_init
+TC_PROGS+= h_inotify_directory
+TC_PROGS+= h_inotify_single_file
+TC_PROGS+= h_inotify_watch_change
+
+.PATH: ${.CURDIR}/arch/${MACHINE_ARCH}
+
+LDFLAGS+= -nostartfiles -static
+CFLAGS+= -I${.CURDIR}/../../../sys
+CFLAGS+= -I${.CURDIR}
+CFLAGS+= -D_STANDALONE
+
+FILESDIR= ${TESTSDIR}
+
+.for _P in ${TESTS_SH}
+.if ${MACHINE_ARCH} == "x86_64"
+TESTS_SH_SRC_${_P}= h_common.sh
+.else
+TESTS_SH_SRC_${_P}= h_not_supported.sh
+.endif
+TESTS_SH_SRC_${_P}+= ${_P}.sh
+.endfor
+
+.for _P in ${TC_PROGS}
+CLEANFILES+= ${_P}
+FILES+= ${_P}
+
+.if ${MACHINE_ARCH} == "x86_64"
+PROGS+= ${_P}.out
+SRCS.${_P}.out= ${_P}.c
+SRCS.${_P}.out+= h_linux.c
+SRCS.${_P}.out+= h_syscall.c
+MAN.${_P}.out= # empty
+FILESMODE_${_P}= ${BINMODE}
+
+proginstall-${_P}.out:
+ # Do not install
+
+${_P}: ${_P}.out
+ ${_MKTARGET_CREATE}
+ cp ${.ALLSRC} ${.TARGET}
+ ${ELFEDIT} --output-osabi Linux ${.TARGET}
+.else
+${_P}:
+ echo '' > ${.TARGET}
+.endif
+
+realall: ${_P}
+.endfor
+
+CLEANFILES+= ${TESTS_SH}
+CLEANFILES+= Atffile
+
+.include <bsd.test.mk>
diff --git a/compat/linux/arch/x86_64/h_syscall.c b/compat/linux/arch/x86_64/h_syscall.c
new file mode 100644
index 000000000000..32485c121a47
--- /dev/null
+++ b/compat/linux/arch/x86_64/h_syscall.c
@@ -0,0 +1,55 @@
+/* $NetBSD: h_syscall.c,v 1.1 2023/08/19 22:56:44 christos Exp $ */
+
+/*-
+ * Copyright (c) 2023 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Theodore Preduta.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (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: h_syscall.c,v 1.1 2023/08/19 22:56:44 christos Exp $");
+#include "h_linux.h"
+
+long
+syscall6(long number, register_t arg1, register_t arg2, register_t arg3,
+ register_t arg4, register_t arg5, register_t arg6, ...)
+{
+ long retval;
+ register register_t r10 __asm__ ("r10") = arg4;
+ register register_t r8 __asm__ ("r8") = arg5;
+ register register_t r9 __asm__ ("r9") = arg6;
+
+ __asm__ __volatile__ ("syscall"
+ : "=a"(retval)
+ : "a"(number), "D"(arg1), "S"(arg2), "d"(arg3), "r"(r10), "r"(r8), "r"(r9)
+ : "rcx", "r11", "memory");
+
+ if (retval < 0) {
+ errno = -retval;
+ return -1;
+ }
+
+ return retval;
+}
diff --git a/compat/linux/h_common.sh b/compat/linux/h_common.sh
new file mode 100644
index 000000000000..79e4d700566c
--- /dev/null
+++ b/compat/linux/h_common.sh
@@ -0,0 +1,34 @@
+# $NetBSD: h_common.sh,v 1.1 2023/08/19 22:56:44 christos Exp $
+#
+# Copyright (c) 2023 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# This code is derived from software contributed to The NetBSD Foundation
+# by Theodore Preduta.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#/
+
+h_ensure_emul_exists() {
+ modstat | grep -q '^compat_linux\W' \
+ || atf_skip "Linux emulation not loaded"
+}
diff --git a/compat/linux/h_inotify_directory.c b/compat/linux/h_inotify_directory.c
new file mode 100644
index 000000000000..3fc8d91e7674
--- /dev/null
+++ b/compat/linux/h_inotify_directory.c
@@ -0,0 +1,108 @@
+/* $NetBSD: h_inotify_directory.c,v 1.1 2023/08/19 22:56:44 christos Exp $ */
+
+/*-
+ * Copyright (c) 2023 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Theodore Preduta.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (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: h_inotify_directory.c,v 1.1 2023/08/19 22:56:44 christos Exp $");
+
+#include "h_linux.h"
+
+#include <sys/null.h>
+
+#include <compat/linux/linux_syscall.h>
+#include <compat/linux/common/linux_inotify.h>
+
+#define INOTIFY_ALL_DIRECTORY (LINUX_IN_ATTRIB|LINUX_IN_CREATE \
+ |LINUX_IN_MOVE_SELF|LINUX_IN_MOVED_FROM \
+ |LINUX_IN_MOVED_TO|LINUX_IN_DELETE \
+ |LINUX_IN_DELETE_SELF)
+
+char buf[8192];
+
+struct {
+ uint32_t mask;
+ bool cookie;
+ char name[16];
+} target_events[] = {
+ { .mask = LINUX_IN_CREATE, .cookie = 0, .name = "test", },
+ { .mask = LINUX_IN_MOVED_FROM, .cookie = 1, .name = "test", },
+ { .mask = LINUX_IN_MOVED_TO, .cookie = 1, .name = "test2", },
+ { .mask = LINUX_IN_DELETE, .cookie = 0, .name = "test2", },
+ { .mask = LINUX_IN_MOVE_SELF, .cookie = 0, .name = "", },
+ { .mask = LINUX_IN_DELETE_SELF, .cookie = 0, .name = "", },
+ { .mask = LINUX_IN_IGNORED, .cookie = 0, .name = "", },
+};
+
+void
+_start(void)
+{
+ int fd, wd, targetfd;
+ char *cur_buf;
+ struct linux_inotify_event *cur_ie;
+
+ RS(mkdir("test", 0644));
+
+ RS(fd = syscall(LINUX_SYS_inotify_init));
+ RS(wd = syscall(LINUX_SYS_inotify_add_watch, fd, (register_t)"test",
+ INOTIFY_ALL_DIRECTORY));
+
+ /* Create some events. */
+ RS(targetfd = open("test/test", LINUX_O_RDWR|LINUX_O_CREAT, 0644));
+ RS(write(targetfd, &targetfd, sizeof(targetfd)));
+ RS(close(targetfd));
+ RS(rename("test/test", "test/test2"));
+ RS(unlink("test/test2"));
+ RS(rename("test", "test2"));
+ RS(rmdir("test2"));
+
+ /* Check the events. */
+ RS(read(fd, buf, sizeof(buf)));
+ cur_buf = buf;
+ for (size_t i = 0; i < __arraycount(target_events); i++) {
+ cur_ie = (struct linux_inotify_event *)cur_buf;
+
+ REQUIRE(cur_ie->wd == wd);
+ REQUIRE(cur_ie->mask == target_events[i].mask);
+
+ if (target_events[i].cookie)
+ REQUIRE(cur_ie->cookie != 0);
+ else
+ REQUIRE(cur_ie->cookie == 0);
+
+ if (target_events[i].name[0] != '\0') {
+ REQUIRE(cur_ie->len > strlen(target_events[i].name));
+ REQUIRE(strcmp(cur_ie->name, target_events[i].name) == 0);
+ } else
+ REQUIRE(cur_ie->len == 0);
+
+ cur_buf += sizeof(struct linux_inotify_event) + cur_ie->len;
+ }
+
+ exit(0);
+}
diff --git a/compat/linux/h_inotify_init.c b/compat/linux/h_inotify_init.c
new file mode 100644
index 000000000000..c1a45d9ad570
--- /dev/null
+++ b/compat/linux/h_inotify_init.c
@@ -0,0 +1,65 @@
+/* $NetBSD: h_inotify_init.c,v 1.1 2023/08/19 22:56:44 christos Exp $ */
+
+/*-
+ * Copyright (c) 2023 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Theodore Preduta.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (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: h_inotify_init.c,v 1.1 2023/08/19 22:56:44 christos Exp $");
+
+#include "h_linux.h"
+
+#include <compat/linux/linux_syscall.h>
+#include <compat/linux/common/linux_errno.h>
+#include <compat/linux/common/linux_fcntl.h>
+#include <compat/linux/common/linux_inotify.h>
+
+void
+_start(void)
+{
+ int fd;
+
+ /* Check that none of CLOEXEC or NONBLOCK are set. */
+ RS(fd = syscall(LINUX_SYS_inotify_init));
+ REQUIRE(fcntl(fd, LINUX_F_GETFD) == 0);
+ REQUIRE((fcntl(fd, LINUX_F_GETFL) & LINUX_O_NONBLOCK) == 0);
+ RS(close(fd));
+
+ /* Check that only NONBLOCK is set. */
+ RS(fd = syscall(LINUX_SYS_inotify_init1, LINUX_IN_NONBLOCK));
+ REQUIRE(fcntl(fd, LINUX_F_GETFD) == 0);
+ REQUIRE((fcntl(fd, LINUX_F_GETFL) & LINUX_O_NONBLOCK) != 0);
+ RS(close(fd));
+
+ /* Check that only CLOEXEC is set. */
+ RS(fd = syscall(LINUX_SYS_inotify_init1, LINUX_IN_CLOEXEC));
+ REQUIRE(fcntl(fd, LINUX_F_GETFD) != 0);
+ REQUIRE((fcntl(fd, LINUX_F_GETFL) & LINUX_O_NONBLOCK) == 0);
+ RS(close(fd));
+
+ exit(0);
+}
diff --git a/compat/linux/h_inotify_single_file.c b/compat/linux/h_inotify_single_file.c
new file mode 100644
index 000000000000..6e2f7125b120
--- /dev/null
+++ b/compat/linux/h_inotify_single_file.c
@@ -0,0 +1,87 @@
+/* $NetBSD: h_inotify_single_file.c,v 1.1 2023/08/19 22:56:44 christos Exp $ */
+
+/*-
+ * Copyright (c) 2023 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Theodore Preduta.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (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: h_inotify_single_file.c,v 1.1 2023/08/19 22:56:44 christos Exp $");
+
+#include "h_linux.h"
+
+#include <compat/linux/linux_syscall.h>
+#include <compat/linux/common/linux_inotify.h>
+
+#define INOTIFY_ALL_FILE (LINUX_IN_ATTRIB|LINUX_IN_CLOSE_NOWRITE \
+ |LINUX_IN_OPEN|LINUX_IN_MOVE_SELF \
+ |LINUX_IN_ACCESS|LINUX_IN_CLOSE_WRITE \
+ |LINUX_IN_MODIFY|LINUX_IN_DELETE_SELF)
+
+struct linux_inotify_event events[10];
+
+void
+_start(void)
+{
+ int fd, wd, targetfd, buf;
+
+ RS(targetfd = open("test", LINUX_O_RDWR|LINUX_O_CREAT, 0644));
+ RS(close(targetfd));
+
+ RS(fd = syscall(LINUX_SYS_inotify_init));
+ RS(wd = syscall(LINUX_SYS_inotify_add_watch, fd, (register_t)"test",
+ INOTIFY_ALL_FILE));
+
+ /* Create some events. */
+ RS(targetfd = open("test", LINUX_O_RDWR|LINUX_O_CREAT, 0644));
+ RS(write(targetfd, &buf, sizeof(buf)));
+ RS(read(targetfd, &buf, sizeof(buf)));
+ RS(close(targetfd));
+ RS(targetfd = open("test", LINUX_O_RDONLY|LINUX_O_CREAT, 0644));
+ RS(close(targetfd));
+ RS(rename("test", "test2"));
+ RS(unlink("test2"));
+
+ /* Get and check the events. */
+ RS(read(fd, events, sizeof(events)));
+
+ for (size_t i = 0; i < __arraycount(events); i++)
+ REQUIRE(events[i].wd == wd && events[i].cookie == 0
+ && events[i].len == 0);
+
+ REQUIRE(events[0].mask == LINUX_IN_OPEN);
+ REQUIRE(events[1].mask == LINUX_IN_MODIFY);
+ REQUIRE(events[2].mask == LINUX_IN_ACCESS);
+ REQUIRE(events[3].mask == LINUX_IN_CLOSE_WRITE);
+ REQUIRE(events[4].mask == LINUX_IN_OPEN);
+ REQUIRE(events[5].mask == LINUX_IN_CLOSE_NOWRITE);
+ REQUIRE(events[6].mask == LINUX_IN_MOVE_SELF);
+ REQUIRE(events[7].mask == LINUX_IN_ATTRIB);
+ REQUIRE(events[8].mask == LINUX_IN_DELETE_SELF);
+ REQUIRE(events[9].mask == LINUX_IN_IGNORED);
+
+ exit(0);
+}
diff --git a/compat/linux/h_inotify_watch_change.c b/compat/linux/h_inotify_watch_change.c
new file mode 100644
index 000000000000..99075bd59940
--- /dev/null
+++ b/compat/linux/h_inotify_watch_change.c
@@ -0,0 +1,88 @@
+/* $NetBSD: h_inotify_watch_change.c,v 1.1 2023/08/19 22:56:44 christos Exp $ */
+
+/*-
+ * Copyright (c) 2023 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Theodore Preduta.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (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: h_inotify_watch_change.c,v 1.1 2023/08/19 22:56:44 christos Exp $");
+
+#include "h_linux.h"
+
+#include <compat/linux/linux_syscall.h>
+#include <compat/linux/common/linux_inotify.h>
+
+struct linux_inotify_event events[2];
+
+void
+_start(void)
+{
+ int fd, wd, targetfd;
+ ssize_t nread;
+
+ RS(targetfd = open("test", LINUX_O_RDWR|LINUX_O_CREAT, 0644));
+ RS(close(targetfd));
+
+ RS(fd = syscall(LINUX_SYS_inotify_init));
+ RS(wd = syscall(LINUX_SYS_inotify_add_watch, fd, (register_t)"test",
+ LINUX_IN_CLOSE_NOWRITE));
+
+ /* We should only get the close event. */
+ RS(targetfd = open("test", LINUX_O_RDONLY|LINUX_O_CREAT, 0644));
+ RS(close(targetfd));
+
+ RS(nread = read(fd, events, sizeof(events)));
+ REQUIRE(nread == sizeof(events[0]));
+ REQUIRE(events[0].mask == LINUX_IN_CLOSE_NOWRITE);
+
+ /* Change the watch descriptor. */
+ RS(wd = syscall(LINUX_SYS_inotify_add_watch, fd, (register_t)"test",
+ LINUX_IN_OPEN));
+
+ /* We should only get the open event. */
+ RS(targetfd = open("test", LINUX_O_RDONLY|LINUX_O_CREAT, 0644));
+ RS(close(targetfd));
+
+ RS(nread = read(fd, events, sizeof(events)));
+ REQUIRE(nread == sizeof(events[0]));
+ REQUIRE(events[0].mask == LINUX_IN_OPEN);
+
+ /* Add to the watch descriptor. */
+ RS(wd = syscall(LINUX_SYS_inotify_add_watch, fd, (register_t)"test",
+ LINUX_IN_CLOSE_NOWRITE|LINUX_IN_MASK_ADD));
+
+ /* Now we should get both the open and the close. */
+ RS(targetfd = open("test", LINUX_O_RDONLY|LINUX_O_CREAT, 0644));
+ RS(close(targetfd));
+
+ RS(nread = read(fd, events, sizeof(events)));
+ REQUIRE(nread == 2 * sizeof(events[0]));
+ REQUIRE(events[0].mask == LINUX_IN_OPEN);
+ REQUIRE(events[1].mask == LINUX_IN_CLOSE_NOWRITE);
+
+ exit(0);
+}
diff --git a/compat/linux/h_linux.c b/compat/linux/h_linux.c
new file mode 100644
index 000000000000..4acbb051626d
--- /dev/null
+++ b/compat/linux/h_linux.c
@@ -0,0 +1,36 @@
+/* $NetBSD: h_linux.c,v 1.1 2023/08/19 22:56:44 christos Exp $ */
+
+/*-
+ * Copyright (c) 2023 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Theodore Preduta.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (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: h_linux.c,v 1.1 2023/08/19 22:56:44 christos Exp $");
+
+#include "h_linux.h"
+
+int errno = 0;
diff --git a/compat/linux/h_linux.h b/compat/linux/h_linux.h
new file mode 100644
index 000000000000..1b21fa6d39fc
--- /dev/null
+++ b/compat/linux/h_linux.h
@@ -0,0 +1,76 @@
+/* $NetBSD: h_linux.h,v 1.2 2023/08/23 20:05:05 rillig Exp $ */
+
+/*-
+ * Copyright (c) 2023 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Theodore Preduta.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (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 SRC_TESTS_COMPAT_LINUX_H_LINUX_H_
+#define SRC_TESTS_COMPAT_LINUX_H_LINUX_H_
+
+#include <sys/types.h> /* For register_t. */
+
+#define FAIL (-1)
+
+#define syscall(number, ...) syscall6(number, ## __VA_ARGS__, \
+ 0, 0, 0, 0, 0, 0)
+
+#define RS(x) do { if ((x) == -1) exit(errno); } while (0)
+#define REQUIRE(x) do { if (!(x)) exit(FAIL); } while (0)
+
+/* Convenience wrappers for common syscalls. */
+#define close(fd) (int)syscall(LINUX_SYS_close, fd)
+#define exit(status) (void)syscall(LINUX_SYS_exit_group, status)
+#define fcntl(fd, cmd, ...) (int)syscall(LINUX_SYS_fcntl, fd, cmd, \
+ ## __VA_ARGS__)
+#define lseek(fd, off, whence) (off_t)syscall(LINUX_SYS_lseek, fd, \
+ (register_t)off, whence)
+#define mkdir(path, mode) (int)syscall(LINUX_SYS_mkdir, \
+ (register_t)path, mode)
+#define open(path, flags, ...) (int)syscall(LINUX_SYS_open, \
+ (register_t)path, flags, \
+ ## __VA_ARGS__)
+#define read(fd, buf, count) (ssize_t)syscall(LINUX_SYS_read, fd, \
+ (register_t)buf, count)
+#define rename(from, to) (int)syscall(LINUX_SYS___posix_rename, \
+ (register_t)from, (register_t)to)
+#define rmdir(path) (int)syscall(LINUX_SYS_rmdir, \
+ (register_t)path)
+#define unlink(path) (int)syscall(LINUX_SYS_unlink, \
+ (register_t)path)
+#define write(fd, buf, count) (ssize_t)syscall(LINUX_SYS_write, fd, \
+ (register_t)buf, count)
+
+/* GCC builtins. */
+#define strcmp(s1, s2) __builtin_strcmp(s1, s2)
+#define strlen(s) __builtin_strlen(s)
+
+long syscall6(long number, register_t, register_t, register_t, register_t,
+ register_t, register_t, ...);
+
+extern int errno;
+
+#endif /* !SRC_TESTS_COMPAT_LINUX_H_LINUX_H_ */
diff --git a/compat/linux/h_not_supported.sh b/compat/linux/h_not_supported.sh
new file mode 100644
index 000000000000..97a8f58e75c1
--- /dev/null
+++ b/compat/linux/h_not_supported.sh
@@ -0,0 +1,33 @@
+# $NetBSD: h_not_supported.sh,v 1.1 2023/08/19 22:56:44 christos Exp $
+#
+# Copyright (c) 2023 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# This code is derived from software contributed to The NetBSD Foundation
+# by Theodore Preduta.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#/
+
+h_ensure_emul_exists() {
+ atf_skip "Linux emulation tests are not supported on this architecture"
+}
diff --git a/compat/linux/t_inotify.sh b/compat/linux/t_inotify.sh
new file mode 100644
index 000000000000..a25931a3b56d
--- /dev/null
+++ b/compat/linux/t_inotify.sh
@@ -0,0 +1,79 @@
+# $NetBSD: t_inotify.sh,v 1.1 2023/08/19 22:56:44 christos Exp $
+#
+# Copyright (c) 2023 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# This code is derived from software contributed to The NetBSD Foundation
+# by Theodore Preduta.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#/
+
+atf_test_case init
+init_head() {
+ atf_set "descr" "Tests inotify_init applies its flags correctly"
+}
+
+init_body() {
+ h_ensure_emul_exists
+ atf_check -s exit:0 "$(atf_get_srcdir)/h_inotify_init"
+}
+
+atf_test_case single_file
+single_file_head() {
+ atf_set "descr" \
+ "Tests correct events are generated when a single file is watched"
+}
+
+single_file_body() {
+ h_ensure_emul_exists
+ atf_check -s exit:0 "$(atf_get_srcdir)/h_inotify_single_file"
+}
+
+atf_test_case directory
+directory_head() {
+ atf_set "descr" \
+ "Tests correct events are generated when a directory is watched"
+}
+
+directory_body() {
+ h_ensure_emul_exists
+ atf_check -s exit:0 "$(atf_get_srcdir)/h_inotify_directory"
+}
+
+atf_test_case watch_change
+watch_change_head() {
+ atf_set "descr" \
+ "Tests the watch descriptor can be modified"
+}
+
+watch_change_body() {
+ h_ensure_emul_exists
+ atf_check -s exit:0 "$(atf_get_srcdir)/h_inotify_watch_change"
+}
+
+atf_init_test_cases() {
+ atf_add_test_case init
+ atf_add_test_case directory
+ atf_add_test_case single_file
+ atf_add_test_case watch_change
+}
diff --git a/crypto/libcrypto/t_sha512trunc.c b/crypto/libcrypto/t_sha512trunc.c
new file mode 100644
index 000000000000..619db68b7a21
--- /dev/null
+++ b/crypto/libcrypto/t_sha512trunc.c
@@ -0,0 +1,170 @@
+/* $NetBSD: t_sha512trunc.c,v 1.2 2024/03/15 18:10:37 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_sha512trunc.c,v 1.2 2024/03/15 18:10:37 riastradh Exp $");
+
+#include <stddef.h>
+
+#include <atf-c.h>
+
+#include <openssl/evp.h>
+
+#include "h_macros.h"
+
+struct testcase {
+ const unsigned char in[128];
+ size_t inlen;
+ const unsigned char out[32];
+};
+
+static void
+check(const struct testcase *C, size_t n, size_t digestlen, const EVP_MD *md)
+{
+ enum { C0 = 0xc0, C1 = 0xc1 };
+ unsigned char *buf, *digest, *p0, *p1;
+ size_t i;
+
+ ATF_REQUIRE_MSG(digestlen <= INT_MAX, "digestlen=%zu", digestlen);
+ ATF_REQUIRE_EQ_MSG((int)digestlen, EVP_MD_size(md),
+ "expected %d, got %d", (int)digestlen, EVP_MD_size(md));
+
+ ATF_REQUIRE_MSG(digestlen < SIZE_MAX - 2048,
+ "digestlen=%zu", digestlen);
+ REQUIRE_LIBC(buf = malloc(digestlen + 2048), NULL);
+ p0 = buf;
+ digest = buf + 1;
+ p1 = buf + 1 + digestlen;
+
+ for (i = 0; i < n; i++) {
+ EVP_MD_CTX *ctx;
+ unsigned digestlen1;
+
+ *p0 = C0;
+ *p1 = C1;
+
+#define REQUIRE(x) ATF_REQUIRE_MSG((x), "i=%zu", i)
+ REQUIRE(ctx = EVP_MD_CTX_new());
+ REQUIRE(EVP_DigestInit_ex(ctx, md, NULL));
+ REQUIRE(EVP_DigestUpdate(ctx, C->in, C->inlen));
+ REQUIRE(EVP_DigestFinal_ex(ctx, digest, &digestlen1));
+#undef REQUIRE
+ ATF_CHECK_MSG(digestlen == digestlen1,
+ "i=%zu: expected %zu got %u", i, digestlen, digestlen1);
+ EVP_MD_CTX_free(ctx);
+
+ ATF_CHECK_MSG(memcmp(digest, C->out, digestlen) == 0,
+ "i=%zu", i);
+
+ ATF_CHECK_EQ_MSG(*p0, C0, "expected 0x%x got 0x%hhx", C0, *p0);
+ ATF_CHECK_EQ_MSG(*p1, C1, "expected 0x%x got 0x%hhx", C1, *p1);
+ }
+}
+
+/*
+ * Test vectors from:
+ *
+ * https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Secure-Hashing#Testing
+ */
+
+ATF_TC(sha512_224);
+ATF_TC_HEAD(sha512_224, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test SHA512-224");
+}
+ATF_TC_BODY(sha512_224, tc)
+{
+ static const struct testcase C[] = {
+ [0] = {
+ .inlen = 0,
+ .out = {
+ 0x6e,0xd0,0xdd,0x02, 0x80,0x6f,0xa8,0x9e,
+ 0x25,0xde,0x06,0x0c, 0x19,0xd3,0xac,0x86,
+ 0xca,0xbb,0x87,0xd6, 0xa0,0xdd,0xd0,0x5c,
+ 0x33,0x3b,0x84,0xf4,
+ },
+ },
+ [1] = {
+ .inlen = 1,
+ .in = {
+ 0xcf,
+ },
+ .out = {
+ 0x41,0x99,0x23,0x9e, 0x87,0xd4,0x7b,0x6f,
+ 0xed,0xa0,0x16,0x80, 0x2b,0xf3,0x67,0xfb,
+ 0x6e,0x8b,0x56,0x55, 0xef,0xf6,0x22,0x5c,
+ 0xb2,0x66,0x8f,0x4a,
+ },
+ },
+ };
+
+ check(C, __arraycount(C), 28, EVP_sha512_224());
+}
+
+ATF_TC(sha512_256);
+ATF_TC_HEAD(sha512_256, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test SHA512-256");
+}
+ATF_TC_BODY(sha512_256, tc)
+{
+ static const struct testcase C[] = {
+ [0] = {
+ .inlen = 0,
+ .out = {
+ 0xc6,0x72,0xb8,0xd1, 0xef,0x56,0xed,0x28,
+ 0xab,0x87,0xc3,0x62, 0x2c,0x51,0x14,0x06,
+ 0x9b,0xdd,0x3a,0xd7, 0xb8,0xf9,0x73,0x74,
+ 0x98,0xd0,0xc0,0x1e, 0xce,0xf0,0x96,0x7a,
+ },
+ },
+ [1] = {
+ .inlen = 1,
+ .in = {
+ 0xfa,
+ },
+ .out = {
+ 0xc4,0xef,0x36,0x92, 0x3c,0x64,0xe5,0x1e,
+ 0x87,0x57,0x20,0xe5, 0x50,0x29,0x8a,0x5a,
+ 0xb8,0xa3,0xf2,0xf8, 0x75,0xb1,0xe1,0xa4,
+ 0xc9,0xb9,0x5b,0xab, 0xf7,0x34,0x4f,0xef,
+ },
+ },
+ };
+
+ check(C, __arraycount(C), 32, EVP_sha512_256());
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, sha512_224);
+ ATF_TP_ADD_TC(tp, sha512_256);
+
+ return atf_no_error();
+}
diff --git a/fs/cd9660/h_hexdump_r.c b/fs/cd9660/h_hexdump_r.c
new file mode 100644
index 000000000000..5436a71b5f8f
--- /dev/null
+++ b/fs/cd9660/h_hexdump_r.c
@@ -0,0 +1,100 @@
+/* $NetBSD: h_hexdump_r.c,v 1.1 2024/04/28 14:39:22 rillig Exp $ */
+
+/*
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code was contributed to The NetBSD Foundation by Roland Illig.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Given the output from "hexdump -C", reconstruct the original file. */
+
+#include <err.h>
+#include <inttypes.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define H "[0-9a-f]"
+#define HH " (" H H ")"
+
+static off_t off, noff;
+static unsigned char prev_bytes[16], bytes[16], zeroes[16];
+
+int
+main(void)
+{
+ char line[81];
+ regex_t data_re, end_re;
+ regmatch_t m[18];
+
+ if (regcomp(&data_re, "^(" H "{8,9})"
+ " " HH HH HH HH HH HH HH HH " " HH HH HH HH HH HH HH HH
+ " \\|.{16}\\|$", REG_EXTENDED) != 0)
+ err(1, "regcomp");
+ if (regcomp(&end_re, "^(" H "{8,9})$", REG_EXTENDED) != 0)
+ err(1, "regcomp");
+
+ while (fgets(line, sizeof(line), stdin) != NULL) {
+ line[strcspn(line, "\n")] = '\0';
+
+ if (strcmp(line, "*") == 0)
+ continue;
+
+ if (regexec(&data_re, line, 18, m, 0) == 0) {
+ noff = (off_t)strtoimax(line + m[1].rm_so, NULL, 16);
+ for (size_t i = 0; i < 16; i++)
+ bytes[i] = (unsigned char)strtoumax(
+ line + m[2 + i].rm_so, NULL, 16);
+
+ } else if (regexec(&end_re, line, 2, m, 0) == 0) {
+ noff = (off_t)strtoimax(line + m[1].rm_so, NULL, 16);
+ if (off < noff) {
+ if (fseeko(stdout, noff - 16, SEEK_SET) != 0)
+ err(1, "fseeko");
+ if (fwrite(prev_bytes, 1, 16, stdout) != 16)
+ err(1, "fwrite");
+ }
+ } else
+ err(1, "invalid line '%s'", line);
+
+ if (memcmp(prev_bytes, zeroes, 16) != 0) {
+ while (off < noff) {
+ if (fwrite(prev_bytes, 1, 16, stdout) != 16)
+ err(1, "fwrite");
+ off += 16;
+ }
+ if (off != noff)
+ err(1, "off");
+ } else {
+ if (fseeko(stdout, noff, SEEK_SET) != 0)
+ err(1, "fseeko");
+ off = noff;
+ }
+
+ memcpy(prev_bytes, bytes, 16);
+ }
+ return 0;
+}
diff --git a/fs/cd9660/pr_48787.image.hex b/fs/cd9660/pr_48787.image.hex
new file mode 100644
index 000000000000..706af997e830
--- /dev/null
+++ b/fs/cd9660/pr_48787.image.hex
@@ -0,0 +1,278 @@
+00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+*
+00008000 01 43 44 30 30 31 01 00 20 20 20 20 20 20 20 20 |.CD001.. |
+00008010 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | |
+00008020 20 20 20 20 20 20 20 20 49 53 4f 49 4d 41 47 45 | ISOIMAGE|
+00008030 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | |
+00008040 20 20 20 20 20 20 20 20 00 00 00 00 00 00 00 00 | ........|
+00008050 f9 41 20 00 00 20 41 f9 00 00 00 00 00 00 00 00 |.A .. A.........|
+00008060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00008070 00 00 00 00 00 00 00 00 01 00 00 01 01 00 00 01 |................|
+00008080 00 08 08 00 14 00 00 00 00 00 00 14 f5 41 20 00 |.............A .|
+00008090 00 00 00 00 00 20 41 f6 00 00 00 00 22 00 f2 41 |..... A....."..A|
+000080a0 20 00 00 20 41 f2 00 08 00 00 00 00 08 00 72 05 | .. A.........r.|
+000080b0 06 0f 1f 21 00 02 00 00 01 00 00 01 01 00 20 20 |...!.......... |
+000080c0 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | |
+*
+000081b0 20 20 20 20 20 20 20 20 20 20 20 20 20 20 58 4f | XO|
+000081c0 52 52 49 53 4f 2d 31 2e 33 2e 37 20 32 30 31 34 |RRISO-1.3.7 2014|
+000081d0 2e 30 35 2e 30 33 2e 31 31 35 30 31 31 2c 20 4c |.05.03.115011, L|
+000081e0 49 42 49 53 4f 42 55 52 4e 2d 31 2e 33 2e 37 2c |IBISOBURN-1.3.7,|
+000081f0 20 4c 49 42 49 53 4f 46 53 2d 31 2e 33 2e 37 2c | LIBISOFS-1.3.7,|
+00008200 20 4c 49 42 42 55 52 4e 2d 31 2e 33 2e 37 20 20 | LIBBURN-1.3.7 |
+00008210 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | |
+*
+00008320 20 20 20 20 20 20 20 20 20 20 20 20 20 32 30 31 | 201|
+00008330 34 30 35 30 36 31 35 33 34 32 38 30 30 00 32 30 |4050615342800.20|
+00008340 31 34 30 35 30 36 31 35 33 34 32 38 30 30 00 30 |14050615342800.0|
+00008350 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 00 |000000000000000.|
+00008360 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
+00008370 00 01 00 20 20 20 20 20 20 20 20 20 20 20 20 20 |... |
+00008380 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | |
+*
+00008570 20 20 20 00 00 00 00 00 00 00 00 00 00 00 00 00 | .............|
+00008580 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+*
+00008800 ff 43 44 30 30 31 01 00 00 00 00 00 00 00 00 00 |.CD001..........|
+00008810 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+*
+00018000 01 43 44 30 30 31 01 00 20 20 20 20 20 20 20 20 |.CD001.. |
+00018010 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | |
+00018020 20 20 20 20 20 20 20 20 49 53 4f 49 4d 41 47 45 | ISOIMAGE|
+00018030 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | |
+00018040 20 20 20 20 20 20 20 20 00 00 00 00 00 00 00 00 | ........|
+00018050 b9 41 20 00 00 20 41 b9 00 00 00 00 00 00 00 00 |.A .. A.........|
+00018060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00018070 00 00 00 00 00 00 00 00 01 00 00 01 01 00 00 01 |................|
+00018080 00 08 08 00 14 00 00 00 00 00 00 14 35 00 00 00 |............5...|
+00018090 00 00 00 00 00 00 00 36 00 00 00 00 22 00 32 00 |.......6....".2.|
+000180a0 00 00 00 00 00 32 00 08 00 00 00 00 08 00 72 05 |.....2........r.|
+000180b0 06 0f 1f 21 00 02 00 00 01 00 00 01 01 00 20 20 |...!.......... |
+000180c0 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | |
+*
+000181b0 20 20 20 20 20 20 20 20 20 20 20 20 20 20 58 4f | XO|
+000181c0 52 52 49 53 4f 2d 31 2e 33 2e 37 20 32 30 31 34 |RRISO-1.3.7 2014|
+000181d0 2e 30 35 2e 30 33 2e 31 31 35 30 31 31 2c 20 4c |.05.03.115011, L|
+000181e0 49 42 49 53 4f 42 55 52 4e 2d 31 2e 33 2e 37 2c |IBISOBURN-1.3.7,|
+000181f0 20 4c 49 42 49 53 4f 46 53 2d 31 2e 33 2e 37 2c | LIBISOFS-1.3.7,|
+00018200 20 4c 49 42 42 55 52 4e 2d 31 2e 33 2e 37 20 20 | LIBBURN-1.3.7 |
+00018210 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | |
+*
+00018320 20 20 20 20 20 20 20 20 20 20 20 20 20 32 30 31 | 201|
+00018330 34 30 35 30 36 31 35 33 31 33 33 30 30 00 32 30 |4050615313300.20|
+00018340 31 34 30 35 30 36 31 35 33 31 33 33 30 30 00 30 |14050615313300.0|
+00018350 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 00 |000000000000000.|
+00018360 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
+00018370 00 01 00 20 20 20 20 20 20 20 20 20 20 20 20 20 |... |
+00018380 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | |
+*
+00018570 20 20 20 00 00 00 00 00 00 00 00 00 00 00 00 00 | .............|
+00018580 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+*
+00018800 ff 43 44 30 30 31 01 00 00 00 00 00 00 00 00 00 |.CD001..........|
+00018810 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+*
+00019000 84 00 32 00 00 00 00 00 00 32 00 08 00 00 00 00 |..2......2......|
+00019010 08 00 72 05 06 0f 1f 21 00 02 00 00 01 00 00 01 |..r....!........|
+00019020 01 00 53 50 07 01 be ef 00 50 58 24 01 ed 41 00 |..SP.....PX$..A.|
+00019030 00 00 00 41 ed 01 00 00 00 00 00 00 01 00 00 00 |...A............|
+00019040 00 00 00 00 00 00 00 00 00 00 00 00 00 54 46 1a |.............TF.|
+00019050 01 0e 72 05 06 0f 1f 21 00 72 05 06 0f 1f 21 00 |..r....!.r....!.|
+00019060 72 05 06 0f 1f 21 00 43 45 1c 01 33 00 00 00 00 |r....!.CE..3....|
+00019070 00 00 33 00 00 00 00 00 00 00 00 ed 00 00 00 00 |..3.............|
+00019080 00 00 ed 00 60 00 32 00 00 00 00 00 00 32 00 08 |....`.2......2..|
+00019090 00 00 00 00 08 00 72 05 06 0f 1f 21 00 02 00 00 |......r....!....|
+000190a0 01 00 00 01 01 01 50 58 24 01 ed 41 00 00 00 00 |......PX$..A....|
+000190b0 41 ed 01 00 00 00 00 00 00 01 00 00 00 00 00 00 |A...............|
+000190c0 00 00 00 00 00 00 00 00 00 00 54 46 1a 01 0e 72 |..........TF...r|
+000190d0 05 06 0f 1f 21 00 72 05 06 0f 1f 21 00 72 05 06 |....!.r....!.r..|
+000190e0 0f 1f 21 00 6a 00 34 00 00 00 00 00 00 34 00 08 |..!.j.4......4..|
+000190f0 00 00 00 00 08 00 72 05 06 0f 1e 12 00 02 00 00 |......r.........|
+00019100 01 00 00 01 02 4d 59 00 50 58 24 01 ed 41 00 00 |.....MY.PX$..A..|
+00019110 00 00 41 ed 01 00 00 00 00 00 00 01 e8 03 00 00 |..A.............|
+00019120 00 00 03 e8 e8 03 00 00 00 00 03 e8 54 46 1a 01 |............TF..|
+00019130 0e 72 05 06 0f 1e 12 00 72 05 06 0f 1a 0e 00 72 |.r......r......r|
+00019140 05 06 0f 1e 12 00 4e 4d 07 01 00 6d 79 00 00 00 |......NM...my...|
+00019150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+*
+00019800 45 52 ed 01 0a 54 87 01 52 52 49 50 5f 31 39 39 |ER...T..RRIP_199|
+00019810 31 41 54 48 45 20 52 4f 43 4b 20 52 49 44 47 45 |1ATHE ROCK RIDGE|
+00019820 20 49 4e 54 45 52 43 48 41 4e 47 45 20 50 52 4f | INTERCHANGE PRO|
+00019830 54 4f 43 4f 4c 20 50 52 4f 56 49 44 45 53 20 53 |TOCOL PROVIDES S|
+00019840 55 50 50 4f 52 54 20 46 4f 52 20 50 4f 53 49 58 |UPPORT FOR POSIX|
+00019850 20 46 49 4c 45 20 53 59 53 54 45 4d 20 53 45 4d | FILE SYSTEM SEM|
+00019860 41 4e 54 49 43 53 50 4c 45 41 53 45 20 43 4f 4e |ANTICSPLEASE CON|
+00019870 54 41 43 54 20 44 49 53 43 20 50 55 42 4c 49 53 |TACT DISC PUBLIS|
+00019880 48 45 52 20 46 4f 52 20 53 50 45 43 49 46 49 43 |HER FOR SPECIFIC|
+00019890 41 54 49 4f 4e 20 53 4f 55 52 43 45 2e 20 20 53 |ATION SOURCE. S|
+000198a0 45 45 20 50 55 42 4c 49 53 48 45 52 20 49 44 45 |EE PUBLISHER IDE|
+000198b0 4e 54 49 46 49 45 52 20 49 4e 20 50 52 49 4d 41 |NTIFIER IN PRIMA|
+000198c0 52 59 20 56 4f 4c 55 4d 45 20 44 45 53 43 52 49 |RY VOLUME DESCRI|
+000198d0 50 54 4f 52 20 46 4f 52 20 43 4f 4e 54 41 43 54 |PTOR FOR CONTACT|
+000198e0 20 49 4e 46 4f 52 4d 41 54 49 4f 4e 2e 00 00 00 | INFORMATION....|
+000198f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+*
+0001a000 60 00 34 00 00 00 00 00 00 34 00 08 00 00 00 00 |`.4......4......|
+0001a010 08 00 72 05 06 0f 1e 12 00 02 00 00 01 00 00 01 |..r.............|
+0001a020 01 00 50 58 24 01 ed 41 00 00 00 00 41 ed 01 00 |..PX$..A....A...|
+0001a030 00 00 00 00 00 01 e8 03 00 00 00 00 03 e8 e8 03 |................|
+0001a040 00 00 00 00 03 e8 54 46 1a 01 0e 72 05 06 0f 1e |......TF...r....|
+0001a050 12 00 72 05 06 0f 1a 0e 00 72 05 06 0f 1e 12 00 |..r......r......|
+0001a060 60 00 32 00 00 00 00 00 00 32 00 08 00 00 00 00 |`.2......2......|
+0001a070 08 00 72 05 06 0f 1f 21 00 02 00 00 01 00 00 01 |..r....!........|
+0001a080 01 01 50 58 24 01 ed 41 00 00 00 00 41 ed 01 00 |..PX$..A....A...|
+0001a090 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 |................|
+0001a0a0 00 00 00 00 00 00 54 46 1a 01 0e 72 05 06 0f 1f |......TF...r....|
+0001a0b0 21 00 72 05 06 0f 1f 21 00 72 05 06 0f 1f 21 00 |!.r....!.r....!.|
+0001a0c0 7c 00 38 00 00 00 00 00 00 38 00 f8 ff ff ff ff ||.8......8......|
+0001a0d0 f8 00 72 05 06 0f 1e 12 00 80 00 00 01 00 00 01 |..r.............|
+0001a0e0 0d 4c 41 52 47 45 5f 46 49 4c 45 2e 3b 31 50 58 |.LARGE_FILE.;1PX|
+0001a0f0 24 01 a4 81 00 00 00 00 81 a4 01 00 00 00 00 00 |$...............|
+0001a100 00 01 e8 03 00 00 00 00 03 e8 e8 03 00 00 00 00 |................|
+0001a110 03 e8 54 46 1a 01 0e 72 05 06 0f 1e 12 00 72 05 |..TF...r......r.|
+0001a120 06 0f 1e 12 00 72 05 06 0f 1e 12 00 4e 4d 0f 01 |.....r......NM..|
+0001a130 00 6c 61 72 67 65 5f 66 69 6c 65 00 7c 00 37 00 |.large_file.|.7.|
+0001a140 20 00 00 20 00 37 00 10 0d 02 02 0d 10 00 72 05 | .. .7........r.|
+0001a150 06 0f 1e 12 00 00 00 00 01 00 00 01 0d 4c 41 52 |.............LAR|
+0001a160 47 45 5f 46 49 4c 45 2e 3b 31 50 58 24 01 a4 81 |GE_FILE.;1PX$...|
+0001a170 00 00 00 00 81 a4 01 00 00 00 00 00 00 01 e8 03 |................|
+0001a180 00 00 00 00 03 e8 e8 03 00 00 00 00 03 e8 54 46 |..............TF|
+0001a190 1a 01 0e 72 05 06 0f 1e 12 00 72 05 06 0f 1e 12 |...r......r.....|
+0001a1a0 00 72 05 06 0f 1e 12 00 4e 4d 0f 01 00 6c 61 72 |.r......NM...lar|
+0001a1b0 67 65 5f 66 69 6c 65 00 00 00 00 00 00 00 00 00 |ge_file.........|
+0001a1c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+*
+0001a800 01 00 32 00 00 00 01 00 00 00 02 00 34 00 00 00 |..2.........4...|
+0001a810 01 00 4d 59 00 00 00 00 00 00 00 00 00 00 00 00 |..MY............|
+0001a820 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+*
+0001b000 01 00 00 00 00 32 00 01 00 00 02 00 00 00 00 34 |.....2.........4|
+0001b010 00 01 4d 59 00 00 00 00 00 00 00 00 00 00 00 00 |..MY............|
+0001b020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+*
+1020f8000 01 43 44 30 30 31 01 00 20 20 20 20 20 20 20 20 |.CD001.. |
+1020f8010 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | |
+1020f8020 20 20 20 20 20 20 20 20 49 53 4f 49 4d 41 47 45 | ISOIMAGE|
+1020f8030 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | |
+1020f8040 20 20 20 20 20 20 20 20 00 00 00 00 00 00 00 00 | ........|
+1020f8050 19 00 00 00 00 00 00 19 00 00 00 00 00 00 00 00 |................|
+1020f8060 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+1020f8070 00 00 00 00 00 00 00 00 01 00 00 01 01 00 00 01 |................|
+1020f8080 00 08 08 00 14 00 00 00 00 00 00 14 f5 41 20 00 |.............A .|
+1020f8090 00 00 00 00 00 20 41 f6 00 00 00 00 22 00 f2 41 |..... A....."..A|
+1020f80a0 20 00 00 20 41 f2 00 08 00 00 00 00 08 00 72 05 | .. A.........r.|
+1020f80b0 06 0f 1f 21 00 02 00 00 01 00 00 01 01 00 20 20 |...!.......... |
+1020f80c0 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | |
+*
+1020f81b0 20 20 20 20 20 20 20 20 20 20 20 20 20 20 58 4f | XO|
+1020f81c0 52 52 49 53 4f 2d 31 2e 33 2e 37 20 32 30 31 34 |RRISO-1.3.7 2014|
+1020f81d0 2e 30 35 2e 30 33 2e 31 31 35 30 31 31 2c 20 4c |.05.03.115011, L|
+1020f81e0 49 42 49 53 4f 42 55 52 4e 2d 31 2e 33 2e 37 2c |IBISOBURN-1.3.7,|
+1020f81f0 20 4c 49 42 49 53 4f 46 53 2d 31 2e 33 2e 37 2c | LIBISOFS-1.3.7,|
+1020f8200 20 4c 49 42 42 55 52 4e 2d 31 2e 33 2e 37 20 20 | LIBBURN-1.3.7 |
+1020f8210 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | |
+*
+1020f8320 20 20 20 20 20 20 20 20 20 20 20 20 20 32 30 31 | 201|
+1020f8330 34 30 35 30 36 31 35 33 34 32 38 30 30 00 32 30 |4050615342800.20|
+1020f8340 31 34 30 35 30 36 31 35 33 34 32 38 30 30 00 30 |14050615342800.0|
+1020f8350 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 00 |000000000000000.|
+1020f8360 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 |0000000000000000|
+1020f8370 00 01 00 20 20 20 20 20 20 20 20 20 20 20 20 20 |... |
+1020f8380 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 | |
+*
+1020f8570 20 20 20 00 00 00 00 00 00 00 00 00 00 00 00 00 | .............|
+1020f8580 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+*
+1020f8800 ff 43 44 30 30 31 01 00 00 00 00 00 00 00 00 00 |.CD001..........|
+1020f8810 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+*
+1020f9000 84 00 f2 41 20 00 00 20 41 f2 00 08 00 00 00 00 |...A .. A.......|
+1020f9010 08 00 72 05 06 0f 1f 21 00 02 00 00 01 00 00 01 |..r....!........|
+1020f9020 01 00 53 50 07 01 be ef 00 50 58 24 01 ed 41 00 |..SP.....PX$..A.|
+1020f9030 00 00 00 41 ed 01 00 00 00 00 00 00 01 00 00 00 |...A............|
+1020f9040 00 00 00 00 00 00 00 00 00 00 00 00 00 54 46 1a |.............TF.|
+1020f9050 01 0e 72 05 06 0f 1f 21 00 72 05 06 0f 1f 21 00 |..r....!.r....!.|
+1020f9060 72 05 06 0f 1f 21 00 43 45 1c 01 f3 41 20 00 00 |r....!.CE...A ..|
+1020f9070 20 41 f3 00 00 00 00 00 00 00 00 ed 00 00 00 00 | A..............|
+1020f9080 00 00 ed 00 60 00 f2 41 20 00 00 20 41 f2 00 08 |....`..A .. A...|
+1020f9090 00 00 00 00 08 00 72 05 06 0f 1f 21 00 02 00 00 |......r....!....|
+1020f90a0 01 00 00 01 01 01 50 58 24 01 ed 41 00 00 00 00 |......PX$..A....|
+1020f90b0 41 ed 01 00 00 00 00 00 00 01 00 00 00 00 00 00 |A...............|
+1020f90c0 00 00 00 00 00 00 00 00 00 00 54 46 1a 01 0e 72 |..........TF...r|
+1020f90d0 05 06 0f 1f 21 00 72 05 06 0f 1f 21 00 72 05 06 |....!.r....!.r..|
+1020f90e0 0f 1f 21 00 6a 00 f4 41 20 00 00 20 41 f4 00 08 |..!.j..A .. A...|
+1020f90f0 00 00 00 00 08 00 72 05 06 0f 1e 12 00 02 00 00 |......r.........|
+1020f9100 01 00 00 01 02 4d 59 00 50 58 24 01 ed 41 00 00 |.....MY.PX$..A..|
+1020f9110 00 00 41 ed 01 00 00 00 00 00 00 01 e8 03 00 00 |..A.............|
+1020f9120 00 00 03 e8 e8 03 00 00 00 00 03 e8 54 46 1a 01 |............TF..|
+1020f9130 0e 72 05 06 0f 1e 12 00 72 05 06 0f 1a 0e 00 72 |.r......r......r|
+1020f9140 05 06 0f 1e 12 00 4e 4d 07 01 00 6d 79 00 7c 00 |......NM...my.|.|
+1020f9150 f8 41 20 00 00 20 41 f8 06 00 00 00 00 00 00 06 |.A .. A.........|
+1020f9160 72 05 06 0f 22 00 00 00 00 00 01 00 00 01 0d 53 |r..."..........S|
+1020f9170 4d 41 4c 4c 5f 46 49 4c 45 2e 3b 31 50 58 24 01 |MALL_FILE.;1PX$.|
+1020f9180 a4 81 00 00 00 00 81 a4 01 00 00 00 00 00 00 01 |................|
+1020f9190 e8 03 00 00 00 00 03 e8 e8 03 00 00 00 00 03 e8 |................|
+1020f91a0 54 46 1a 01 0e 72 05 06 0f 22 00 00 72 05 06 0f |TF...r..."..r...|
+1020f91b0 22 00 00 72 05 06 0f 22 00 00 4e 4d 0f 01 00 73 |"..r..."..NM...s|
+1020f91c0 6d 61 6c 6c 5f 66 69 6c 65 00 00 00 00 00 00 00 |mall_file.......|
+1020f91d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+*
+1020f9800 45 52 ed 01 0a 54 87 01 52 52 49 50 5f 31 39 39 |ER...T..RRIP_199|
+1020f9810 31 41 54 48 45 20 52 4f 43 4b 20 52 49 44 47 45 |1ATHE ROCK RIDGE|
+1020f9820 20 49 4e 54 45 52 43 48 41 4e 47 45 20 50 52 4f | INTERCHANGE PRO|
+1020f9830 54 4f 43 4f 4c 20 50 52 4f 56 49 44 45 53 20 53 |TOCOL PROVIDES S|
+1020f9840 55 50 50 4f 52 54 20 46 4f 52 20 50 4f 53 49 58 |UPPORT FOR POSIX|
+1020f9850 20 46 49 4c 45 20 53 59 53 54 45 4d 20 53 45 4d | FILE SYSTEM SEM|
+1020f9860 41 4e 54 49 43 53 50 4c 45 41 53 45 20 43 4f 4e |ANTICSPLEASE CON|
+1020f9870 54 41 43 54 20 44 49 53 43 20 50 55 42 4c 49 53 |TACT DISC PUBLIS|
+1020f9880 48 45 52 20 46 4f 52 20 53 50 45 43 49 46 49 43 |HER FOR SPECIFIC|
+1020f9890 41 54 49 4f 4e 20 53 4f 55 52 43 45 2e 20 20 53 |ATION SOURCE. S|
+1020f98a0 45 45 20 50 55 42 4c 49 53 48 45 52 20 49 44 45 |EE PUBLISHER IDE|
+1020f98b0 4e 54 49 46 49 45 52 20 49 4e 20 50 52 49 4d 41 |NTIFIER IN PRIMA|
+1020f98c0 52 59 20 56 4f 4c 55 4d 45 20 44 45 53 43 52 49 |RY VOLUME DESCRI|
+1020f98d0 50 54 4f 52 20 46 4f 52 20 43 4f 4e 54 41 43 54 |PTOR FOR CONTACT|
+1020f98e0 20 49 4e 46 4f 52 4d 41 54 49 4f 4e 2e 00 00 00 | INFORMATION....|
+1020f98f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+*
+1020fa000 60 00 f4 41 20 00 00 20 41 f4 00 08 00 00 00 00 |`..A .. A.......|
+1020fa010 08 00 72 05 06 0f 1e 12 00 02 00 00 01 00 00 01 |..r.............|
+1020fa020 01 00 50 58 24 01 ed 41 00 00 00 00 41 ed 01 00 |..PX$..A....A...|
+1020fa030 00 00 00 00 00 01 e8 03 00 00 00 00 03 e8 e8 03 |................|
+1020fa040 00 00 00 00 03 e8 54 46 1a 01 0e 72 05 06 0f 1e |......TF...r....|
+1020fa050 12 00 72 05 06 0f 1a 0e 00 72 05 06 0f 1e 12 00 |..r......r......|
+1020fa060 60 00 f2 41 20 00 00 20 41 f2 00 08 00 00 00 00 |`..A .. A.......|
+1020fa070 08 00 72 05 06 0f 1f 21 00 02 00 00 01 00 00 01 |..r....!........|
+1020fa080 01 01 50 58 24 01 ed 41 00 00 00 00 41 ed 01 00 |..PX$..A....A...|
+1020fa090 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 |................|
+1020fa0a0 00 00 00 00 00 00 54 46 1a 01 0e 72 05 06 0f 1f |......TF...r....|
+1020fa0b0 21 00 72 05 06 0f 1f 21 00 72 05 06 0f 1f 21 00 |!.r....!.r....!.|
+1020fa0c0 7c 00 38 00 00 00 00 00 00 38 00 f8 ff ff ff ff ||.8......8......|
+1020fa0d0 f8 00 72 05 06 0f 1e 12 00 80 00 00 01 00 00 01 |..r.............|
+1020fa0e0 0d 4c 41 52 47 45 5f 46 49 4c 45 2e 3b 31 50 58 |.LARGE_FILE.;1PX|
+1020fa0f0 24 01 a4 81 00 00 00 00 81 a4 01 00 00 00 00 00 |$...............|
+1020fa100 00 01 e8 03 00 00 00 00 03 e8 e8 03 00 00 00 00 |................|
+1020fa110 03 e8 54 46 1a 01 0e 72 05 06 0f 1e 12 00 72 05 |..TF...r......r.|
+1020fa120 06 0f 1e 12 00 72 05 06 0f 1e 12 00 4e 4d 0f 01 |.....r......NM..|
+1020fa130 00 6c 61 72 67 65 5f 66 69 6c 65 00 7c 00 37 00 |.large_file.|.7.|
+1020fa140 20 00 00 20 00 37 00 10 0d 02 02 0d 10 00 72 05 | .. .7........r.|
+1020fa150 06 0f 1e 12 00 00 00 00 01 00 00 01 0d 4c 41 52 |.............LAR|
+1020fa160 47 45 5f 46 49 4c 45 2e 3b 31 50 58 24 01 a4 81 |GE_FILE.;1PX$...|
+1020fa170 00 00 00 00 81 a4 01 00 00 00 00 00 00 01 e8 03 |................|
+1020fa180 00 00 00 00 03 e8 e8 03 00 00 00 00 03 e8 54 46 |..............TF|
+1020fa190 1a 01 0e 72 05 06 0f 1e 12 00 72 05 06 0f 1e 12 |...r......r.....|
+1020fa1a0 00 72 05 06 0f 1e 12 00 4e 4d 0f 01 00 6c 61 72 |.r......NM...lar|
+1020fa1b0 67 65 5f 66 69 6c 65 00 00 00 00 00 00 00 00 00 |ge_file.........|
+1020fa1c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+*
+1020fa800 01 00 f2 41 20 00 01 00 00 00 02 00 f4 41 20 00 |...A ........A .|
+1020fa810 01 00 4d 59 00 00 00 00 00 00 00 00 00 00 00 00 |..MY............|
+1020fa820 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+*
+1020fb000 01 00 00 20 41 f2 00 01 00 00 02 00 00 20 41 f4 |... A........ A.|
+1020fb010 00 01 4d 59 00 00 00 00 00 00 00 00 00 00 00 00 |..MY............|
+1020fb020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+*
+1020fc000 68 65 6c 6c 6f 0a 00 00 00 00 00 00 00 00 00 00 |hello...........|
+1020fc010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+*
+102150000
diff --git a/fs/lfs/t_basic.c b/fs/lfs/t_basic.c
new file mode 100644
index 000000000000..3a01a5a43569
--- /dev/null
+++ b/fs/lfs/t_basic.c
@@ -0,0 +1,79 @@
+/* $NetBSD: t_basic.c,v 1.1 2025/10/13 00:44:35 perseant Exp $ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+#include "h_macros.h"
+#include "util.h"
+
+#define FSSIZE 10000
+
+/* Actually run the test */
+void test(int);
+
+ATF_TC(newfs_fsck32);
+ATF_TC_HEAD(newfs_fsck32, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "LFS32 newfs_lfs produces a filesystem that passes fsck_lfs");
+ atf_tc_set_md_var(tc, "timeout", "20");
+}
+
+ATF_TC(newfs_fsck64);
+ATF_TC_HEAD(newfs_fsck64, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "LFS64 newfs_lfs produces a filesystem that passes fsck_lfs");
+ atf_tc_set_md_var(tc, "timeout", "20");
+}
+
+ATF_TC_BODY(newfs_fsck32, tc)
+{
+ test(32);
+}
+
+ATF_TC_BODY(newfs_fsck64, tc)
+{
+ test(64);
+}
+
+void test(int width)
+{
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ /*
+ * Initialize.
+ */
+
+ /* Create image file larger than filesystem */
+ create_lfs(FSSIZE, FSSIZE, width, 1);
+
+ if (fsck())
+ atf_tc_fail_errno("fsck found errors after first unmount");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, newfs_fsck32);
+ ATF_TP_ADD_TC(tp, newfs_fsck64);
+ return atf_no_error();
+}
diff --git a/fs/lfs/t_fcntl.c b/fs/lfs/t_fcntl.c
new file mode 100644
index 000000000000..0d7c1f86d79f
--- /dev/null
+++ b/fs/lfs/t_fcntl.c
@@ -0,0 +1,407 @@
+/* $NetBSD: t_fcntl.c,v 1.6 2025/12/10 03:08:52 perseant Exp $ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+#include "h_macros.h"
+#include "util.h"
+
+/* Debugging conditions */
+/* #define FORCE_SUCCESS */ /* Don't actually revert, everything worked */
+/* #define USE_DUMPLFS */ /* Dump the filesystem at certain steps */
+
+#define UNCHANGED_CONTROL MP "/3-a-random-file"
+#define FSSIZE 10000 /* In sectors */
+#define A_FEW_BLOCKS 6500 /* In bytes; a few blocks worth */
+#define MORE_THAN_A_SEGMENT (4 * SEGSIZE) /* In bytes; > SEGSIZE */
+
+/* Set up filesystem with a file in it */
+int setup(int, struct ufs_args *, off_t);
+
+/* Actually run the test */
+void coalesce(int);
+void cleanseg(int);
+void autoclean(int);
+
+/* Unmount and check fsck */
+void teardown(int, struct ufs_args *, off_t);
+
+ATF_TC(coalesce32);
+ATF_TC_HEAD(coalesce32, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "LFS32 LFCNSCRAMBLE/LFCNREWRITEFILE");
+ /* atf_tc_set_md_var(tc, "timeout", "20"); */
+}
+
+ATF_TC(coalesce64);
+ATF_TC_HEAD(coalesce64, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "LFS64 LFCNSCRAMBLE/LFCNREWRITEFILE");
+ /* atf_tc_set_md_var(tc, "timeout", "20"); */
+}
+
+ATF_TC(cleanseg32);
+ATF_TC_HEAD(cleanseg32, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "LFS32 LFCNREWRITESEG");
+ atf_tc_set_md_var(tc, "timeout", "20");
+}
+
+ATF_TC(cleanseg64);
+ATF_TC_HEAD(cleanseg64, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "LFS64 LFCNREWRITESEG");
+ atf_tc_set_md_var(tc, "timeout", "20");
+}
+
+ATF_TC(autoclean32);
+ATF_TC_HEAD(autoclean32, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "LFS32 LFCNAUTOCLEAN");
+ /* atf_tc_set_md_var(tc, "timeout", "20"); */
+}
+
+ATF_TC(autoclean64);
+ATF_TC_HEAD(autoclean64, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "LFS64 LFCNAUTOCLEAN");
+ /* atf_tc_set_md_var(tc, "timeout", "20"); */
+}
+
+ATF_TC_BODY(coalesce32, tc)
+{
+ coalesce(32);
+}
+
+ATF_TC_BODY(coalesce64, tc)
+{
+ coalesce(64);
+}
+
+ATF_TC_BODY(cleanseg32, tc)
+{
+ cleanseg(32);
+}
+
+ATF_TC_BODY(cleanseg64, tc)
+{
+ cleanseg(64);
+}
+
+ATF_TC_BODY(autoclean32, tc)
+{
+ autoclean(32);
+}
+
+ATF_TC_BODY(autoclean64, tc)
+{
+ autoclean(64);
+}
+
+int setup(int width, struct ufs_args *argsp, off_t filesize)
+{
+ int fd;
+
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ /*
+ * Initialize.
+ */
+
+ /* Create image file */
+ create_lfs(FSSIZE, FSSIZE, width, 1);
+
+ /* Mount filesystem */
+ fprintf(stderr, "* Mount fs [1]\n");
+ memset(argsp, 0, sizeof *argsp);
+ argsp->fspec = __UNCONST(FAKEBLK);
+ if (rump_sys_mount(MOUNT_LFS, MP, 0, argsp, sizeof *argsp) == -1)
+ atf_tc_fail_errno("rump_sys_mount failed");
+
+ /* Payload */
+ fprintf(stderr, "* Initial payload\n");
+ write_file(UNCHANGED_CONTROL, filesize, 1, 3);
+
+ /* Make the data go to disk */
+ fprintf(stderr, "* Double sync\n");
+ rump_sys_sync();
+ rump_sys_sync();
+
+ /* Get a handle to the root of the file system */
+ fd = rump_sys_open(MP, O_RDONLY);
+ if (fd < 0)
+ atf_tc_fail_errno("rump_sys_open mount point root failed");
+
+ return fd;
+}
+
+void teardown(int fd, struct ufs_args *argsp, off_t filesize)
+{
+ /* Close descriptor */
+ rump_sys_close(fd);
+
+ /* Unmount */
+ if (rump_sys_unmount(MP, 0) < 0)
+ atf_tc_fail_errno("rump_sys_unmount failed[1]");
+
+ /* Fsck */
+ fprintf(stderr, "* Fsck after final unmount\n");
+ if (fsck())
+ atf_tc_fail("fsck found errors after final unmount");
+
+ /*
+ * Check file system contents
+ */
+
+ /* Mount filesystem one last time */
+ fprintf(stderr, "* Mount fs again to check contents\n");
+ if (rump_sys_mount(MOUNT_LFS, MP, 0, argsp, sizeof *argsp) == -1)
+ atf_tc_fail_errno("rump_sys_mount failed [4]");
+
+ if (check_file(UNCHANGED_CONTROL, filesize, 3) != 0)
+ atf_tc_fail("Unchanged control file differs(!)");
+
+ /* Umount filesystem */
+ if (rump_sys_unmount(MP, 0) < 0)
+ atf_tc_fail_errno("rump_sys_unmount failed[2]");
+
+ /* Final fsck to double check */
+ fprintf(stderr, "* Fsck after final unmount\n");
+ if (fsck())
+ atf_tc_fail("fsck found errors after final unmount");
+}
+
+void coalesce(int width)
+{
+ struct ufs_args args;
+ struct stat statbuf;
+ struct lfs_filestat_req fsr;
+ struct lfs_filestats fss_before, fss_scrambled, fss_after;
+ struct lfs_inode_array inotbl;
+ int fd;
+ ino_t ino;
+
+ fd = setup(width, &args, A_FEW_BLOCKS);
+
+ /* Get our file's inode number */
+ if (rump_sys_stat(UNCHANGED_CONTROL, &statbuf) != 0)
+ atf_tc_fail_errno("rump_sys_stat failed");
+ ino = statbuf.st_ino;
+
+ fprintf(stderr, "Treating inode %d\n", (int)ino);
+
+ /* Retrieve fragmentation statistics */
+ memset(&fss_before, 0, sizeof fss_before);
+ memset(&fss_scrambled, 0, sizeof fss_scrambled);
+ memset(&fss_after, 0, sizeof fss_after);
+
+ fsr.ino = ino;
+ fsr.len = 1;
+ fsr.fss = &fss_before;
+ if (rump_sys_fcntl(fd, LFCNFILESTATS, &fsr) < 0)
+ atf_tc_fail_errno("LFCNFILESTATS[1] failed");
+
+ inotbl.len = 1;
+ inotbl.inodes = &ino;
+
+ fprintf(stderr, "Start ino %d nblk %d count %d total %d\n",
+ (int)ino, (int)fss_before.nblk, (int)fss_before.dc_count,
+ (int)fss_before.dc_sum);
+
+ /* Scramble */
+ if (rump_sys_fcntl(fd, LFCNSCRAMBLE, &inotbl) < 0)
+ atf_tc_fail_errno("LFCNSCRAMBLE failed");
+ fsr.fss = &fss_scrambled;
+ if (rump_sys_fcntl(fd, LFCNFILESTATS, &fsr) < 0)
+ atf_tc_fail_errno("LFCNFILESTATS[2] failed");
+ if (fss_scrambled.dc_count <= fss_before.dc_count)
+ atf_tc_fail("Scramble did not worsen gap count");
+ if (fss_scrambled.dc_sum <= fss_before.dc_sum)
+ atf_tc_fail("Scramble did not worsen total gap length");
+
+ fprintf(stderr, "Scrambled ino %d nblk %d count %d total %d\n",
+ (int)ino, (int)fss_scrambled.nblk, (int)fss_scrambled.dc_count,
+ (int)fss_scrambled.dc_sum);
+
+ /* Coalesce */
+ if (rump_sys_fcntl(fd, LFCNREWRITEFILE, &inotbl) < 0)
+ atf_tc_fail_errno("LFCNREWRITEFILE failed");
+ fsr.fss = &fss_after;
+ if (rump_sys_fcntl(fd, LFCNFILESTATS, &fsr) < 0)
+ atf_tc_fail_errno("LFCNFILESTATS[3] failed");
+ if (fss_scrambled.dc_count <= fss_after.dc_count)
+ atf_tc_fail("Rewrite did not improve gap count");
+ if (fss_scrambled.dc_sum <= fss_after.dc_sum)
+ atf_tc_fail("Rewrite did not improve total gap length");
+
+ fprintf(stderr, "Coalesced ino %d nblk %d count %d total %d\n",
+ (int)ino, (int)fss_after.nblk, (int)fss_after.dc_count,
+ (int)fss_after.dc_sum);
+
+ teardown(fd, &args, A_FEW_BLOCKS);
+}
+
+void cleanseg(int width)
+{
+ struct ufs_args args;
+ struct lfs_segnum_array sna;
+ struct lfs_seguse_array sua;
+ int fd, sn;
+
+ fd = setup(width, &args, MORE_THAN_A_SEGMENT);
+
+ fprintf(stderr, "* Get seguse\n");
+ sua.len = LFS_SEGUSE_MAXCNT;
+ sua.start = 0;
+ sua.seguse = malloc(LFS_SEGUSE_MAXCNT * sizeof(*sua.seguse));
+ if (rump_sys_fcntl(fd, LFCNSEGUSE, &sua) < 0)
+ atf_tc_fail_errno("LFCNSEGUSE[1] failed");
+
+ for (sn = 0; sn < (int)sua.len; ++sn) {
+ if (sua.seguse[sn].su_nbytes == 0)
+ continue;
+ if ((sua.seguse[sn].su_flags & (SEGUSE_DIRTY | SEGUSE_ACTIVE))
+ != SEGUSE_DIRTY)
+ continue;
+ break;
+ }
+ if (sn == (int)sua.len)
+ atf_tc_fail("No segments found to clean");
+
+ fprintf(stderr, "* Cleaning segment #%d\n", sn);
+
+ memset(&sna, 0, sizeof sna);
+ sna.len = 1;
+ sna.segments = &sn;
+ if (rump_sys_fcntl(fd, LFCNREWRITESEGS, &sna) < 0)
+ atf_tc_fail_errno("LFCNREWRITESEGS failed");
+
+ fprintf(stderr, "* Double sync\n");
+ rump_sys_sync();
+ rump_sys_sync();
+
+ fprintf(stderr, "* Get seguse again\n");
+ sua.len = 1;
+ sua.start = sn;
+ if (rump_sys_fcntl(fd, LFCNSEGUSE, &sua) < 0)
+ atf_tc_fail_errno("LFCNSEGUSE[2] failed");
+
+ if (sua.seguse[0].su_nbytes > 0)
+ atf_tc_fail("Empty bytes after clean");
+
+ fprintf(stderr, "* Teardown\n");
+ teardown(fd, &args, MORE_THAN_A_SEGMENT);
+}
+
+void autoclean(int width)
+{
+ struct ufs_args args;
+ struct lfs_autoclean_params autoclean;
+ int i, fd;
+ char filename[1024];
+ struct statvfs statbuf;
+ CLEANERINFO64 ci_before, ci_after;
+ unsigned long target;
+
+ fd = setup(width, &args, CHUNKSIZE);
+
+ rump_sys_fstatvfs1(fd, &statbuf, ST_WAIT);
+ target = 7 * statbuf.f_blocks / 8;
+
+ /* Write a number of files, deleting every other one. */
+ fprintf(stderr, "* Write numbered files\n");
+ for (i = 4; ; i++) {
+ fprintf(stderr, "* create %d\n", i);
+ sprintf(filename, "%s/%d", MP, i);
+ write_file(filename, SEGSIZE / 3, 1, 0);
+ rump_sys_fstatvfs1(fd, &statbuf, ST_WAIT);
+ if (statbuf.f_bfree < target)
+ break;
+ fprintf(stderr, " %ld blocks vs target %ld\n",
+ (long)statbuf.f_bfree, (long)target);
+ }
+ rump_sys_sync();
+ rump_sys_sync();
+
+ /* Record cleanerinfo */
+ fprintf(stderr, "* Get cleanerinfo\n");
+ if (rump_sys_fcntl(fd, LFCNCLEANERINFO, &ci_before) < 0)
+ atf_tc_fail_errno("LFCNCLEANERINFO[1] failed");
+
+ /* Start autocleaner */
+ autoclean.size = sizeof(autoclean);
+ autoclean.mode = LFS_CLEANMODE_GREEDY;
+ autoclean.thresh = -1;
+ autoclean.target = -1;
+ if (rump_sys_fcntl(fd, LFCNAUTOCLEAN, &autoclean) < 0)
+ atf_tc_fail_errno("LFCNAUTOCLEAN[1] failed");
+
+ /* Make many segments almost empty */
+ fprintf(stderr, "* Delete most files\n");
+ for (; i > 0; i--) {
+ if (i % 3 == 0)
+ continue;
+ fprintf(stderr, "* delete %d\n", i);
+ sprintf(filename, "%s/%d", MP, i);
+ rump_sys_unlink(filename);
+ }
+ rump_sys_sync();
+ rump_sys_sync();
+
+ /* Give the cleaner a chance to run */
+ fprintf(stderr, "* Sleep\n");
+ sleep(5);
+
+ /* Auto reclaim freed segments */
+ fprintf(stderr, "* Sync\n");
+ rump_sys_sync();
+ rump_sys_sync();
+
+ /* Record cleanerinfo again */
+ fprintf(stderr, "* Get cleanerinfo again\n");
+ if (rump_sys_fcntl(fd, LFCNCLEANERINFO, &ci_after) < 0)
+ atf_tc_fail_errno("LFCNCLEANERINFO[2] failed");
+
+ /* Compare */
+ if (ci_before.avail >= ci_after.avail)
+ atf_tc_fail("No improvement");
+
+ teardown(fd, &args, CHUNKSIZE);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, coalesce32);
+ ATF_TP_ADD_TC(tp, coalesce64);
+ ATF_TP_ADD_TC(tp, cleanseg32);
+ ATF_TP_ADD_TC(tp, cleanseg64);
+ ATF_TP_ADD_TC(tp, autoclean32);
+ ATF_TP_ADD_TC(tp, autoclean64);
+ return atf_no_error();
+}
diff --git a/fs/lfs/t_orphan.c b/fs/lfs/t_orphan.c
new file mode 100644
index 000000000000..c1cf7f1f71b8
--- /dev/null
+++ b/fs/lfs/t_orphan.c
@@ -0,0 +1,203 @@
+/* $NetBSD: t_orphan.c,v 1.4 2025/12/19 20:58:08 perseant Exp $ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+#include "h_macros.h"
+#include "util.h"
+
+/* Debugging conditions */
+/* #define FORCE_SUCCESS */ /* Don't actually revert, everything worked */
+/* #define USE_DUMPLFS */ /* Dump the filesystem at certain steps */
+
+#define UNCHANGED_CONTROL MP "/3-a-random-file"
+#define FSSIZE 10000 /* In sectors */
+#define FILE_SIZE 65000 /* In bytes; a few blocks worth */
+#define TMPFILE "filehandle"
+
+/* Actually run the test */
+void orphan(int);
+
+ATF_TC(orphan32);
+ATF_TC_HEAD(orphan32, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "LFS32 orphan removal");
+ atf_tc_set_md_var(tc, "timeout", "20");
+}
+
+ATF_TC(orphan64);
+ATF_TC_HEAD(orphan64, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "LFS64 orphan removal");
+ atf_tc_set_md_var(tc, "timeout", "20");
+}
+
+ATF_TC_BODY(orphan32, tc)
+{
+ orphan(32);
+}
+
+ATF_TC_BODY(orphan64, tc)
+{
+ orphan(64);
+}
+
+void orphan(int width)
+{
+ char s[MAXLINE];
+ struct ufs_args args;
+ struct stat statbuf;
+ int fd, status;
+ pid_t childpid;
+ FILE *fp;
+ int thisinum, version, found;
+ ino_t inum;
+
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ /*
+ * Initialize.
+ */
+
+ /* Create image file */
+ create_lfs(FSSIZE, FSSIZE, width, 0);
+
+ /* Prepare to mount */
+ memset(&args, 0, sizeof args);
+ args.fspec = __UNCONST(FAKEBLK);
+
+ if ((childpid = fork()) == 0) {
+ /* Set up rump */
+ rump_init();
+ if (rump_sys_mkdir(MP, 0777) == -1)
+ atf_tc_fail_errno("cannot create mountpoint");
+ rump_pub_etfs_register(FAKEBLK, IMGNAME, RUMP_ETFS_BLK);
+
+ /* Mount filesystem */
+ fprintf(stderr, "* Mount fs [1]\n");
+ if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof args) == -1)
+ atf_tc_fail_errno("rump_sys_mount failed");
+
+ /* Payload */
+ fprintf(stderr, "* Initial payload\n");
+ fd = write_file(UNCHANGED_CONTROL, FILE_SIZE, 0, 0);
+
+ /* Make the data go to disk */
+ rump_sys_sync();
+ rump_sys_sync();
+
+ /* Write the inode number into a temporary file */
+ rump_sys_fstat(fd, &statbuf);
+ fprintf(stderr, "Inode is => %d <=\n", (int)statbuf.st_ino);
+
+ fp = fopen(TMPFILE, "wb");
+ fwrite(&statbuf.st_ino, sizeof(ino_t), 1, fp);
+ fclose(fp);
+
+ /* Delete while still open */
+ if (rump_sys_unlink(UNCHANGED_CONTROL) != 0)
+ atf_tc_fail_errno("rump_sys_unlink failed");
+
+ /* Sanity check values */
+ if (statbuf.st_size != FILE_SIZE)
+ atf_tc_fail("wrong size in initial stat");
+ if (statbuf.st_nlink <= 0)
+ atf_tc_fail("file already deleted");
+ if (statbuf.st_blocks <= 0)
+ atf_tc_fail("no blocks");
+
+ /* Make the data go to disk */
+ rump_sys_sync();
+ rump_sys_sync();
+
+ /* Simulate a system crash */
+ exit(0);
+ }
+
+ /* Wait for child to terminate */
+ waitpid(childpid, &status, 0);
+
+ /* If it died, die ourselves */
+ if (WEXITSTATUS(status))
+ exit(WEXITSTATUS(status));
+
+ /* Fsck */
+ fprintf(stderr, "* Fsck after crash\n");
+ if (fsck())
+ atf_tc_fail("fsck found errors after crash");
+
+ /* Read the inode number from temporary file */
+ fp = fopen(TMPFILE, "rb");
+ fread(&inum, sizeof(ino_t), 1, fp);
+ fclose(fp);
+
+ fprintf(stderr, "Seeking inum => %d <=\n", (int)inum);
+
+ /* Set up rump */
+ rump_init();
+ if (rump_sys_mkdir(MP, 0777) == -1)
+ atf_tc_fail_errno("cannot create mountpoint");
+ rump_pub_etfs_register(FAKEBLK, IMGNAME, RUMP_ETFS_BLK);
+
+ /* Remount */
+ fprintf(stderr, "* Mount fs [2]\n");
+ if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof args) == -1)
+ atf_tc_fail_errno("rump_sys_mount failed[2]");
+
+ /* At this point the orphan should be deleted. */
+
+ /* Unmount */
+ fprintf(stderr, "* Unmount\n");
+ if (rump_sys_unmount(MP, 0) != 0)
+ atf_tc_fail_errno("rump_sys_unmount failed[1]");
+
+ /* Check that it was in fact deleted. */
+ fprintf(stderr, "* Check for orphaned file\n");
+ found = 0;
+ fp = popen("dumplfs -i -s9 ./" IMGNAME, "r");
+ while (fgets(s, MAXLINE, fp) != NULL) {
+ if (sscanf(s, "%d FREE %d", &thisinum, &version) == 2
+ && (ino_t)thisinum == inum) {
+ found = 1;
+ break;
+ }
+ }
+ fclose(fp);
+ if (!found)
+ atf_tc_fail("orphaned inode not freed on subsequent mount");
+
+ /* Fsck */
+ fprintf(stderr, "* Fsck after final unmount\n");
+ if (fsck())
+ atf_tc_fail("fsck found errors after final unmount");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, orphan32);
+ ATF_TP_ADD_TC(tp, orphan64);
+ return atf_no_error();
+}
+
diff --git a/fs/lfs/t_resize.c b/fs/lfs/t_resize.c
new file mode 100644
index 000000000000..0dae56429261
--- /dev/null
+++ b/fs/lfs/t_resize.c
@@ -0,0 +1,180 @@
+/* $NetBSD: t_resize.c,v 1.2 2025/10/30 15:30:17 perseant Exp $ */
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/lfs/lfs.h>
+#include <ufs/lfs/lfs_extern.h>
+
+#include "h_macros.h"
+#include "util.h"
+
+/* Debugging conditions */
+/* #define FORCE_SUCCESS */ /* Don't actually revert, everything worked */
+/* #define USE_DUMPLFS */ /* Dump the filesystem at certain steps */
+
+#define UNCHANGED_CONTROL MP "/3-a-random-file"
+#define BIGSIZE 15000
+#define SMALLSIZE 10000
+__CTASSERT(BIGSIZE > SMALLSIZE);
+
+/* Resize filesystem */
+void resize(int, size_t);
+
+/* Actually run the test */
+void test(int);
+
+ATF_TC(resize32);
+ATF_TC_HEAD(resize32, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "LFS32 resize_lfs creates an inconsistent filesystem");
+ atf_tc_set_md_var(tc, "timeout", "20");
+}
+
+ATF_TC(resize64);
+ATF_TC_HEAD(resize64, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "LFS64 resize_lfs creates an inconsistent filesystem");
+ atf_tc_set_md_var(tc, "timeout", "20");
+}
+
+ATF_TC_BODY(resize32, tc)
+{
+ test(32);
+}
+
+ATF_TC_BODY(resize64, tc)
+{
+ test(64);
+}
+
+void test(int width)
+{
+ struct ufs_args args;
+ int fd;
+
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ /*
+ * Initialize.
+ */
+
+ /* Create image file larger than filesystem */
+ create_lfs(BIGSIZE, SMALLSIZE, width, 1);
+
+ /* Mount filesystem */
+ fprintf(stderr, "* Mount fs [1]\n");
+ memset(&args, 0, sizeof(args));
+ args.fspec = __UNCONST(FAKEBLK);
+ if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof(args)) == -1)
+ atf_tc_fail_errno("rump_sys_mount failed");
+
+ /* Payload */
+ fprintf(stderr, "* Initial payload\n");
+ write_file(UNCHANGED_CONTROL, CHUNKSIZE, 1, 0);
+
+ /* Unmount */
+ rump_sys_unmount(MP, 0);
+ if (fsck())
+ atf_tc_fail_errno("fsck found errors after first unmount");
+
+ /*
+ * Remount and resize.
+ */
+
+ /* Reconfigure and mount filesystem again */
+ fprintf(stderr, "* Remount fs [2, to enlarge]\n");
+ if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof(args)) == -1)
+ atf_tc_fail_errno("rump_sys_mount failed [2]");
+
+ /* Get a handle to the root of the file system */
+ fd = rump_sys_open(MP, O_RDONLY);
+ if (fd < 0)
+ atf_tc_fail_errno("rump_sys_open mount point root failed");
+
+ /* Enlarge filesystem */
+ fprintf(stderr, "* Resize (enlarge)\n");
+ resize(fd, BIGSIZE);
+
+ /* Unmount fs and check */
+ rump_sys_close(fd);
+ rump_sys_unmount(MP, 0);
+ if (fsck())
+ atf_tc_fail_errno("fsck found errors after enlarge");
+
+ /* Mount filesystem for shrink */
+ fprintf(stderr, "* Mount fs [3, to shrink]\n");
+ if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof(args)) == -1)
+ atf_tc_fail_errno("rump_sys_mount failed [3]");
+
+ /* Get a handle to the root of the file system */
+ fd = rump_sys_open(MP, O_RDONLY);
+ if (fd < 0)
+ atf_tc_fail_errno("rump_sys_open mount point root failed");
+
+ /* Shrink filesystem */
+ fprintf(stderr, "* Resize (shrink)\n");
+ resize(fd, SMALLSIZE);
+
+ /* Unmount and check again */
+ rump_sys_close(fd);
+ if (rump_sys_unmount(MP, 0) != 0)
+ atf_tc_fail_errno("rump_sys_umount failed after shrink");
+ fprintf(stderr, "* Fsck after shrink\n");
+ if (fsck())
+ atf_tc_fail("fsck found errors after shrink");
+
+ /*
+ * Check file system contents
+ */
+
+ /* Mount filesystem one last time */
+ fprintf(stderr, "* Mount fs [4, to check contents]\n");
+ if (rump_sys_mount(MOUNT_LFS, MP, 0, &args, sizeof(args)) == -1)
+ atf_tc_fail_errno("rump_sys_mount failed [4]");
+
+ if (check_file(UNCHANGED_CONTROL, CHUNKSIZE, 0) != 0)
+ atf_tc_fail("Unchanged control file differs(!)");
+
+ /* Umount filesystem */
+ rump_sys_unmount(MP, 0);
+
+ /* Final fsck to double check */
+ fprintf(stderr, "* Fsck after final unmount\n");
+ if (fsck())
+ atf_tc_fail("fsck found errors after final unmount");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, resize32);
+ ATF_TP_ADD_TC(tp, resize64);
+ return atf_no_error();
+}
+
+void
+resize(int fd, size_t size)
+{
+ int newnseg = (size * DEV_BSIZE) / SEGSIZE;
+
+ if (rump_sys_fcntl(fd, LFCNRESIZE, &newnseg) != 0)
+ atf_tc_fail_errno("LFCNRESIZE failed");
+}
diff --git a/fs/lfs/util.c b/fs/lfs/util.c
new file mode 100644
index 000000000000..75ca59983596
--- /dev/null
+++ b/fs/lfs/util.c
@@ -0,0 +1,181 @@
+/* $NetBSD: util.c,v 1.6 2025/12/19 20:58:08 perseant Exp $ */
+
+#include <sys/mount.h>
+
+#include <ctype.h>
+#include <fcntl.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_macros.h"
+#include "util.h"
+
+long long sbaddr[2] = { -1, -1 };
+
+/* Create filesystem, note superblock locations */
+void create_lfs(size_t imgsize, size_t fssize, int width, int do_setup)
+{
+ FILE *pipe;
+ char cmd[MAXLINE];
+ char buf[MAXLINE];
+
+ /* Create image file larger than filesystem */
+ sprintf(cmd, "dd if=/dev/zero of=%s bs=512 count=%zd",
+ IMGNAME, imgsize);
+ if (system(cmd) == -1)
+ atf_tc_fail_errno("create image failed");
+
+ /* Create filesystem */
+ fprintf(stderr, "* Create file system\n");
+ sprintf(cmd, "newfs_lfs -D -F -B %d -s %zd -w%d ./%s > %s",
+ SEGSIZE, fssize, width, IMGNAME, LOGFILE);
+ if (system(cmd) == -1)
+ atf_tc_fail_errno("newfs failed");
+ pipe = fopen(LOGFILE, "r");
+ if (pipe == NULL)
+ atf_tc_fail_errno("newfs failed to execute");
+ while (fgets(buf, MAXLINE, pipe) != NULL) {
+ if (sscanf(buf, "%lld,%lld", sbaddr, sbaddr + 1) == 2)
+ break;
+ }
+ while (fgets(buf, MAXLINE, pipe) != NULL)
+ ;
+ fclose(pipe);
+ if (sbaddr[0] < 0 || sbaddr[1] < 0)
+ atf_tc_fail("superblock not found");
+ fprintf(stderr, "* Superblocks at %lld and %lld\n",
+ sbaddr[0], sbaddr[1]);
+
+ if (do_setup) {
+ /* Set up rump */
+ rump_init();
+ if (rump_sys_mkdir(MP, 0777) == -1)
+ atf_tc_fail_errno("cannot create mountpoint");
+ rump_pub_etfs_register(FAKEBLK, IMGNAME, RUMP_ETFS_BLK);
+ }
+}
+
+/* Write some data into a file */
+int write_file(const char *filename, off_t len, int close, unsigned int seed)
+{
+ int fd;
+ unsigned i, j;
+ struct stat statbuf;
+ int flags = O_CREAT|O_WRONLY;
+ char buf[1024];
+ off_t size;
+
+ srandom(seed);
+ if (rump_sys_stat(filename, &statbuf) < 0)
+ size = 0;
+ else {
+ size = statbuf.st_size;
+ flags |= O_APPEND;
+
+ /* Reset randomness */
+ for (i = 0; i < size; i++)
+ random();
+ }
+
+ fd = rump_sys_open(filename, flags);
+
+ for (i = 0; i < len; i+= sizeof(buf)) {
+ for (j = 0; j < sizeof(buf); j++)
+ buf[j] = ((unsigned)random()) & 0xff;
+ rump_sys_write(fd, buf, MIN(len - i, (off_t)sizeof(buf)));
+ }
+
+ if (close) {
+ rump_sys_close(fd);
+ fd = -1;
+ }
+
+ return fd;
+}
+
+/* Check file's existence, size and contents */
+int check_file(const char *filename, int size, unsigned int seed)
+{
+ int fd, i, j, res;
+ struct stat statbuf;
+ unsigned char b, buf[1024];
+
+ if (rump_sys_stat(filename, &statbuf) < 0) {
+ fprintf(stderr, "%s: stat failed\n", filename);
+ return 1;
+ }
+ if (size != statbuf.st_size) {
+ fprintf(stderr, "%s: expected %d bytes, found %d\n",
+ filename, size, (int)statbuf.st_size);
+ return 2;
+ }
+
+ fd = rump_sys_open(filename, O_RDONLY);
+
+ srandom(seed);
+ for (i = 0; i < size; i += sizeof(buf)) {
+ res = MIN(size - i, (off_t)sizeof(buf));
+ rump_sys_read(fd, buf, res);
+ for (j = 0; j < res; j++) {
+ b = (((unsigned)random()) & 0xff);
+ if (buf[j] != b) {
+ fprintf(stderr, "%s: byte %d:"
+ " expected %hhx found %hhx\n",
+ filename, i + j,
+ b, buf[j]);
+ rump_sys_close(fd);
+ return 3;
+ }
+ }
+ }
+ rump_sys_close(fd);
+ fprintf(stderr, "%s: no problem\n", filename);
+ return 0;
+}
+
+/* Run a file system consistency check */
+int fsck(void)
+{
+ char s[MAXLINE];
+ int i, errors = 0;
+ FILE *pipe;
+ char cmd[MAXLINE];
+
+ for (i = 0; i < 2; i++) {
+ sprintf(cmd, "fsck_lfs -n -a -b %jd -f " IMGNAME,
+ (intmax_t)sbaddr[i]);
+ pipe = popen(cmd, "r");
+ while (fgets(s, MAXLINE, pipe) != NULL) {
+ if (isdigit((int)s[0])) /* "5 files ... " */
+ continue;
+ if (isspace((int)s[0]) || s[0] == '*')
+ continue;
+ if (strncmp(s, "Alternate", 9) == 0)
+ continue;
+ if (strncmp(s, "ROLL ", 5) == 0)
+ continue;
+ fprintf(stderr, "FSCK[sb@%lld]: %s", sbaddr[i], s);
+ ++errors;
+ }
+ pclose(pipe);
+ if (errors) {
+ break;
+ }
+ }
+
+ return errors;
+}
+
+/* Run dumplfs */
+void dumplfs()
+{
+ char s[MAXLINE];
+ FILE *pipe;
+
+ pipe = popen("dumplfs -S -s 2 -s 1 -s 0 " IMGNAME, "r");
+ while (fgets(s, MAXLINE, pipe) != NULL)
+ fprintf(stderr, "DUMPLFS: %s", s);
+ pclose(pipe);
+}
+
diff --git a/fs/lfs/util.h b/fs/lfs/util.h
new file mode 100644
index 000000000000..b33f89d084ab
--- /dev/null
+++ b/fs/lfs/util.h
@@ -0,0 +1,28 @@
+/* $NetBSD: util.h,v 1.4 2025/10/30 15:30:17 perseant Exp $ */
+
+/* Create test image and filesystem, record superblock locations */
+void create_lfs(size_t, size_t, int, int);
+
+/* Write a well-known byte pattern into a file, appending if it exists */
+int write_file(const char *, off_t, int, unsigned int);
+
+/* Check the byte pattern and size of the file */
+int check_file(const char *, int, unsigned int);
+
+/* Check the file system for consistency */
+int fsck(void);
+
+/* Run dumplfs; for debugging */
+void dumplfs(void);
+
+#define MAXLINE 132
+#define CHUNKSIZE 300
+#define SEGSIZE 32768
+
+#define IMGNAME "disk.img"
+#define FAKEBLK "/dev/blk"
+#define LOGFILE "newfs.log"
+
+#define MP "/mp"
+
+extern long long sbaddr[2];
diff --git a/games/t_morse.sh b/games/t_morse.sh
new file mode 100644
index 000000000000..5bec82ebbfc7
--- /dev/null
+++ b/games/t_morse.sh
@@ -0,0 +1,127 @@
+# $NetBSD: t_morse.sh,v 1.3 2024/10/12 20:44:11 rillig Exp $
+#
+# Copyright (c) 2024 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+atf_test_case digits
+digits_head()
+{
+ atf_set 'require.progs' '/usr/games/morse'
+}
+digits_body()
+{
+ trailing_space=' '
+ morse_s_digits="\
+ -----
+ .----
+ ..---
+ ...--
+ ....-
+ .....
+ -....
+ --...
+ ---..
+ ----.
+$trailing_space
+ ...-.-
+"
+ atf_check -o "inline:$morse_s_digits" \
+ /usr/games/morse -s 0123456789
+
+ morse_digits="\
+ daw daw daw daw daw
+ dit daw daw daw daw
+ dit dit daw daw daw
+ dit dit dit daw daw
+ dit dit dit dit daw
+ dit dit dit dit dit
+ daw dit dit dit dit
+ daw daw dit dit dit
+ daw daw daw dit dit
+ daw daw daw daw dit
+
+ dit dit dit daw dit daw
+"
+ atf_check -o "inline:$morse_digits" \
+ /usr/games/morse 0123456789
+}
+
+
+# Before 2024-06-16, non-ASCII characters invoked undefined behavior,
+# possibly crashing morse.
+atf_test_case nonascii
+nonascii_head()
+{
+ atf_set 'require.progs' '/usr/games/morse'
+}
+nonascii_body()
+{
+ expected="\
+
+ dit dit dit daw dit daw
+"
+ atf_check -o "inline:$expected" \
+ /usr/games/morse äöü
+}
+
+
+atf_test_case roundtrip
+roundtrip_head() {
+ atf_set 'require.progs' '/usr/games/morse'
+}
+roundtrip_body()
+{
+ # Most punctuation is ignored during encoding.
+ # Missing are: !#$%&*;<>[\]^{|}~
+
+ input=\
+' !"#$%&'\''()*+,-./'\
+'0123456789:;<=>?'\
+'@ABCDEFGHIJKLMNO'\
+'PQRSTUVWXYZ[\]^_'\
+'`abcdefghijklmno'\
+'pqrstuvwxyz{|}~'
+
+ expected=\
+' "'\''()+,-./'\
+'0123456789:=?'\
+'@ABCDEFGHIJKLMNO'\
+'PQRSTUVWXYZ_'\
+'ABCDEFGHIJKLMNO'\
+'PQRSTUVWXYZ \n'
+
+ atf_check -o 'save:roundtrip.morse' \
+ /usr/games/morse -s "$input"
+ atf_check -o "inline:$expected" \
+ /usr/games/morse -d < roundtrip.morse
+}
+
+
+atf_init_test_cases()
+{
+ atf_add_test_case digits
+ atf_add_test_case nonascii
+ atf_add_test_case roundtrip
+}
diff --git a/include/t_stddef.c b/include/t_stddef.c
new file mode 100644
index 000000000000..b5f2c89b86a4
--- /dev/null
+++ b/include/t_stddef.c
@@ -0,0 +1,127 @@
+/* $NetBSD: t_stddef.c,v 1.1 2025/04/01 00:33:55 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Include <stddef.h> first to verify it declares everything we need.
+ */
+#include <stddef.h>
+
+#if __STDC_VERSION__ - 0 >= 202311L
+#if __STDC_VERSION_STDDEF_H__ - 0 < 202311L
+#error __STDC_VERSION_STDDEF_H__ not defined appropriately
+#endif
+#endif
+
+typedef ptrdiff_t nbtest_ptrdiff_t;
+typedef size_t nbtest_size_t;
+#if __STDC_VERSION__ - 0 >= 201112L
+typedef max_align_t nbtest_max_align_t;
+#endif
+typedef wchar_t nbtest_wchar_t;
+#if __STDC_VERSION__ - 0 >= 202311L
+typedef nullptr_t nbtest_nullptr_t;
+#endif
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_stddef.c,v 1.1 2025/04/01 00:33:55 riastradh Exp $");
+
+#include <atf-c.h>
+#include <stdalign.h>
+
+ATF_TC(macros);
+ATF_TC_HEAD(macros, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test <stddef.h> macros work");
+}
+ATF_TC_BODY(macros, tc)
+{
+ void *volatile pNULL = NULL;
+#if __STDC_VERSION__ - 0 >= 202311L
+ void *volatile pnullptr = nullptr;
+#endif
+ struct s { char x[3], y; };
+ size_t o;
+
+ ATF_CHECK(!pNULL);
+#if __STDC_VERSION__ - 0 >= 202311L
+ ATF_CHECK(!pnullptr);
+#endif
+
+#if __STDC_VERSION__ - 0 >= 202311L
+ volatile enum { A, B } x = A;
+ switch (x) {
+ case A:
+ break;
+ case B:
+ default:
+ unreachable();
+ }
+#endif
+
+ ATF_CHECK_MSG((o = offsetof(struct s, y)) == 3,
+ "o=%zu", o);
+}
+
+ATF_TC(types);
+ATF_TC_HEAD(types, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test <stddef.h> types are reasonable");
+}
+ATF_TC_BODY(types, tc)
+{
+
+#ifdef __GNUC__
+ char *p, *q;
+ ATF_CHECK(__builtin_types_compatible_p(ptrdiff_t, typeof(p - q)));
+ ATF_CHECK(__builtin_types_compatible_p(size_t, typeof(sizeof(p))));
+#if __STDC_VERSION__ - 0 >= 202311L
+ ATF_CHECK(__builtin_types_compatible_p(nullptr_t, typeof(nullptr)));
+#endif
+#endif
+
+#if __STDC_VERSION__ - 0 >= 201112L
+ size_t a;
+ ATF_CHECK_MSG((a = alignof(max_align_t)) >= alignof(long long),
+ "a=%zu", a);
+ ATF_CHECK_MSG((a = alignof(max_align_t)) >= alignof(long double),
+ "a=%zu", a);
+ ATF_CHECK_MSG((a = alignof(max_align_t)) >= alignof(void *),
+ "a=%zu", a);
+ ATF_CHECK_MSG((a = alignof(max_align_t)) >= alignof(int (*)(void)),
+ "a=%zu", a);
+#endif
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, macros);
+ ATF_TP_ADD_TC(tp, types);
+
+ return atf_no_error();
+}
diff --git a/kernel/arch/aarch64/contextspfunc.S b/kernel/arch/aarch64/contextspfunc.S
new file mode 100644
index 000000000000..9a4e3059a8e0
--- /dev/null
+++ b/kernel/arch/aarch64/contextspfunc.S
@@ -0,0 +1,46 @@
+/* $NetBSD: contextspfunc.S,v 1.2 2025/06/08 18:55:35 christos Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: contextspfunc.S,v 1.2 2025/06/08 18:55:35 christos Exp $")
+
+/*
+ * contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+ENTRY(contextspfunc)
+ mov x0, sp
+ adrp x1, _C_LABEL(contextsp)
+ str x0, [x1, :lo12:_C_LABEL(contextsp)]
+ b _C_LABEL(contextdone)
+END(contextspfunc)
diff --git a/kernel/arch/aarch64/execregs.c b/kernel/arch/aarch64/execregs.c
new file mode 100644
index 000000000000..4c7697fe98c7
--- /dev/null
+++ b/kernel/arch/aarch64/execregs.c
@@ -0,0 +1,226 @@
+/* $NetBSD: execregs.c,v 1.1 2025/02/27 00:55:31 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: execregs.c,v 1.1 2025/02/27 00:55:31 riastradh Exp $");
+
+#include "execregs.h"
+
+#include <errno.h>
+#include <spawn.h>
+#include <stddef.h>
+#include <unistd.h>
+
+extern char **environ;
+
+static unsigned long
+nonnull(unsigned long x)
+{
+
+ x |= x << 8;
+ x |= x << 16;
+ x |= x << 32;
+ return x;
+}
+
+int
+execregschild(char *path)
+{
+ /* x0: used to pass exec arg0, nonnull anyway (path) */
+ /* x1: used to pass exec arg1, nonnull anyway (argv) */
+ /* x2: used to pass exec arg2, nonnull anyway (environ) */
+ register long x3 __asm("x3") = nonnull(3);
+ register long x4 __asm("x4") = nonnull(4);
+ register long x5 __asm("x5") = nonnull(5);
+ register long x6 __asm("x6") = nonnull(6);
+ register long x7 __asm("x7") = nonnull(7);
+ register long x8 __asm("x8") = nonnull(8);
+ register long x9 __asm("x9") = nonnull(9);
+ register long x10 __asm("x10") = nonnull(10);
+ register long x11 __asm("x11") = nonnull(11);
+ register long x12 __asm("x12") = nonnull(12);
+ register long x13 __asm("x13") = nonnull(13);
+ register long x14 __asm("x14") = nonnull(14);
+ register long x15 __asm("x15") = nonnull(15);
+ register long x16 __asm("x16") = nonnull(16);
+ register long x17 __asm("x17") = nonnull(17);
+ register long x18 __asm("x18") = nonnull(18);
+ register long x19 __asm("x19") = nonnull(19);
+ register long x20 __asm("x20") = nonnull(20);
+ register long x21 __asm("x21") = nonnull(21);
+ register long x22 __asm("x22") = nonnull(22);
+ register long x23 __asm("x23") = nonnull(23);
+ register long x24 __asm("x24") = nonnull(24);
+ register long x25 __asm("x25") = nonnull(25);
+ register long x26 __asm("x26") = nonnull(26);
+ register long x27 __asm("x27") = nonnull(27);
+ register long x28 __asm("x28") = nonnull(28);
+ /* x29: frame pointer, nonnull anyway */
+ /* x30: link register, nonnull anyway */
+
+ char *argv[] = {path, NULL};
+ char **envp = environ;
+
+ /*
+ * Not perfect -- compiler might use some registers for
+ * stack/argument transfers, but all the arguments are nonnull
+ * so this is probably a good test anyway.
+ */
+ __asm volatile("" :
+ "+r"(x3),
+ "+r"(x4),
+ "+r"(x5),
+ "+r"(x6),
+ "+r"(x7),
+ "+r"(x8),
+ "+r"(x9),
+ "+r"(x10),
+ "+r"(x11),
+ "+r"(x12),
+ "+r"(x13),
+ "+r"(x14),
+ "+r"(x15),
+ "+r"(x16),
+ "+r"(x17)
+ :: "memory");
+ /* pacify gcc error: more than 30 operands in 'asm' */
+ __asm volatile("" :
+ "+r"(x18),
+ "+r"(x19),
+ "+r"(x20),
+ "+r"(x21),
+ "+r"(x22),
+ "+r"(x23),
+ "+r"(x24),
+ "+r"(x25),
+ "+r"(x26),
+ "+r"(x27),
+ "+r"(x28)
+ :: "memory");
+
+ return execve(path, argv, envp);
+}
+
+pid_t
+spawnregschild(char *path, int fd)
+{
+ /* x0: used to pass posix_spawn arg0, nonnull anyway (&pid) */
+ /* x1: used to pass posix_spawn arg1, nonnull anyway (path) */
+ /* x2: used to pass posix_spawn arg2, nonnull anyway (&fileacts) */
+ /* x3: used to pass posix_spawn arg3, nonnull anyway (&attr) */
+ /* x4: used to pass posix_spawn arg3, nonnull anyway (argv) */
+ /* x5: used to pass posix_spawn arg3, nonnull anyway (environ) */
+ register long x6 __asm("x6") = nonnull(6);
+ register long x7 __asm("x7") = nonnull(7);
+ register long x8 __asm("x8") = nonnull(8);
+ register long x9 __asm("x9") = nonnull(9);
+ register long x10 __asm("x10") = nonnull(10);
+ register long x11 __asm("x11") = nonnull(11);
+ register long x12 __asm("x12") = nonnull(12);
+ register long x13 __asm("x13") = nonnull(13);
+ register long x14 __asm("x14") = nonnull(14);
+ register long x15 __asm("x15") = nonnull(15);
+ register long x16 __asm("x16") = nonnull(16);
+ register long x17 __asm("x17") = nonnull(17);
+ register long x18 __asm("x18") = nonnull(18);
+ register long x19 __asm("x19") = nonnull(19);
+ register long x20 __asm("x20") = nonnull(20);
+ register long x21 __asm("x21") = nonnull(21);
+ register long x22 __asm("x22") = nonnull(22);
+ register long x23 __asm("x23") = nonnull(23);
+ register long x24 __asm("x24") = nonnull(24);
+ register long x25 __asm("x25") = nonnull(25);
+ register long x26 __asm("x26") = nonnull(26);
+ register long x27 __asm("x27") = nonnull(27);
+ register long x28 __asm("x28") = nonnull(28);
+ /* x29: frame pointer, nonnull anyway */
+ /* x30: link register, nonnull anyway */
+
+ char *argv[] = {path, NULL};
+ char **envp = environ;
+ posix_spawn_file_actions_t fileacts;
+ posix_spawnattr_t attr;
+ pid_t pid;
+ int error;
+
+ error = posix_spawn_file_actions_init(&fileacts);
+ if (error)
+ goto out;
+ error = posix_spawn_file_actions_adddup2(&fileacts, fd, STDOUT_FILENO);
+ if (error)
+ goto out;
+ error = posix_spawnattr_init(&attr);
+ if (error)
+ goto out;
+
+ /*
+ * Not perfect -- compiler might use some registers for
+ * stack/argument transfers, but all the arguments are nonnull
+ * so this is probably a good test anyway.
+ */
+ __asm volatile("" :
+ "+r"(x6),
+ "+r"(x7),
+ "+r"(x8),
+ "+r"(x9),
+ "+r"(x10),
+ "+r"(x11),
+ "+r"(x12),
+ "+r"(x13),
+ "+r"(x14),
+ "+r"(x15),
+ "+r"(x16),
+ "+r"(x17),
+ "+r"(x18),
+ "+r"(x19),
+ "+r"(x20)
+ :: "memory");
+ /* pacify gcc error: more than 30 operands in 'asm' */
+ __asm volatile("" :
+ "+r"(x21),
+ "+r"(x22),
+ "+r"(x23),
+ "+r"(x24),
+ "+r"(x25),
+ "+r"(x26),
+ "+r"(x27),
+ "+r"(x28)
+ :: "memory");
+
+ error = posix_spawn(&pid, path, &fileacts, &attr, argv, envp);
+ if (error)
+ goto out;
+
+out: posix_spawnattr_destroy(&attr);
+ posix_spawn_file_actions_destroy(&fileacts);
+ if (error) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/kernel/arch/aarch64/execregs.h b/kernel/arch/aarch64/execregs.h
new file mode 100644
index 000000000000..471a3859c4d1
--- /dev/null
+++ b/kernel/arch/aarch64/execregs.h
@@ -0,0 +1,86 @@
+/* $NetBSD: execregs.h,v 1.1 2025/02/27 00:55:31 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TESTS_KERNEL_ARCH_AARCH64_EXECREGS_H
+#define TESTS_KERNEL_ARCH_AARCH64_EXECREGS_H
+
+#include <sys/cdefs.h>
+
+#define NEXECREGS 31
+
+#ifndef _LOCORE
+
+#include <unistd.h>
+
+/*
+ * Ordered by struct reg in sys/arch/aarch64/include/reg.h for
+ * convenience of auditing. Must match h_execregs.S.
+ */
+static const char *const regname[] = {
+ "x0",
+ "x1",
+ /* x2: ps_strings */
+ "x3",
+ "x4",
+ "x5",
+ "x6",
+ "x7",
+ "x8",
+ "x9",
+ "x10",
+ "x11",
+ "x12",
+ "x13",
+ "x14",
+ "x15",
+ "x16",
+ "x17",
+ "x18",
+ "x19",
+ "x20",
+ "x21",
+ "x22",
+ "x23",
+ "x24",
+ "x25",
+ "x26",
+ "x27",
+ "x28",
+ "x29",
+ "x30",
+ "tpidr",
+};
+
+__CTASSERT(NEXECREGS == __arraycount(regname));
+
+int execregschild(char *);
+pid_t spawnregschild(char *, int);
+
+#endif /* _LOCORE */
+
+#endif /* TESTS_KERNEL_ARCH_AARCH64_EXECREGS_H */
diff --git a/kernel/arch/aarch64/execsp.S b/kernel/arch/aarch64/execsp.S
new file mode 100644
index 000000000000..52c1472afef6
--- /dev/null
+++ b/kernel/arch/aarch64/execsp.S
@@ -0,0 +1,99 @@
+/* $NetBSD: execsp.S,v 1.3 2025/06/04 19:25:45 christos Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: execsp.S,v 1.3 2025/06/04 19:25:45 christos Exp $")
+
+/*
+ * void execsp_start(void (*cleanup@x0)(void), void *obj_main@x1,
+ * struct ps_strings *ps_strings@x2)
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ */
+ENTRY(execsp_start)
+ mov x16, sp
+ adrp x17, _C_LABEL(startsp)
+ str x16, [x17, :lo12:_C_LABEL(startsp)]
+ b _C_LABEL(__start)
+END(execsp_start)
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+ENTRY(execsp_ctor)
+ mov x16, sp
+ adrp x17, _C_LABEL(ctorsp)
+ str x16, [x17, :lo12:_C_LABEL(ctorsp)]
+ ret
+END(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .section .init_array,"aw",%init_array
+ .p2align 3
+ .xword _C_LABEL(execsp_ctor)
+
+/*
+ * int main(int argc@x0, char **argv@x1, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+ENTRY(main)
+ mov x16, sp
+ adrp x17, _C_LABEL(mainsp)
+ str x16, [x17, :lo12:_C_LABEL(mainsp)]
+ mov x0, #0
+ ret
+END(main)
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+ENTRY(execsp_dtor)
+ mov x16, sp
+ adrp x17, _C_LABEL(dtorsp)
+ str x16, [x17, :lo12:_C_LABEL(dtorsp)]
+ b _C_LABEL(execsp_main)
+END(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .section .fini_array,"aw",%fini_array
+ .p2align 3
+ .xword _C_LABEL(execsp_dtor)
diff --git a/kernel/arch/aarch64/h_execregs.S b/kernel/arch/aarch64/h_execregs.S
new file mode 100644
index 000000000000..f060911067f4
--- /dev/null
+++ b/kernel/arch/aarch64/h_execregs.S
@@ -0,0 +1,84 @@
+/* $NetBSD: h_execregs.S,v 1.2 2025/04/25 12:58:40 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <sys/syscall.h>
+
+#include <machine/asm.h>
+
+#include "execregs.h"
+
+ENTRY(execregs_start)
+ /* create a stack frame with NEXECREGS*8 bytes, aligned to 16-byte */
+ stp fp, lr, [sp, #-(16 + ((NEXECREGS*8 + 15)/16)*16)]!
+
+ /* store registers to buffer on stack */
+ stp x0, x1, [sp, #16] /* order matches execregs.h */
+ /* x2: ps_strings */
+ stp x3, x4, [sp, #(16 + 1*2*8)]
+ stp x5, x6, [sp, #(16 + 2*2*8)]
+ stp x7, x8, [sp, #(16 + 3*2*8)]
+ stp x9, x10, [sp, #(16 + 4*2*8)]
+ stp x11, x12, [sp, #(16 + 5*2*8)]
+ stp x13, x14, [sp, #(16 + 6*2*8)]
+ stp x15, x16, [sp, #(16 + 7*2*8)]
+ stp x17, x18, [sp, #(16 + 8*2*8)]
+ stp x19, x20, [sp, #(16 + 9*2*8)]
+ stp x21, x22, [sp, #(16 + 10*2*8)]
+ stp x23, x24, [sp, #(16 + 11*2*8)]
+ stp x25, x26, [sp, #(16 + 12*2*8)]
+ stp x27, x28, [sp, #(16 + 13*2*8)]
+ stp x29, x30, [sp, #(16 + 14*2*8)]
+ mrs x0, tpidr_el0
+ str x0, [sp, #(16 + 15*2*8)]
+
+ /* call write(STDOUT_FILENO, regs, sizeof(regs)) */
+ mov x0, #1 /* arg0 := STDOUT_FILENO */
+ add x1, sp, #16 /* arg1 := regs */
+ mov x2, #(NEXECREGS*8) /* arg2 := sizeof(regs) */
+ svc #SYS_write
+
+ b.cs 2f /* bail if write failed */
+ cmp x0, #(NEXECREGS*8) /* bail if wrote wrong # of bytes */
+ b.ne 2f
+
+ /* call exit(0) */
+ mov x0, #0 /* arg0 := 0 */
+1: svc #SYS_exit
+ brk #0xffff /* paranoia */
+
+2: /* call exit(127) */
+ mov x0, #127 /* arg0 := 127 */
+ b 1b
+END(execregs_start)
+
+/* main stub to simplify linking */
+ENTRY(main)
+ brk #0xffff
+END(main)
diff --git a/kernel/arch/aarch64/signalsphandler.S b/kernel/arch/aarch64/signalsphandler.S
new file mode 100644
index 000000000000..246ed383273d
--- /dev/null
+++ b/kernel/arch/aarch64/signalsphandler.S
@@ -0,0 +1,46 @@
+/* $NetBSD: signalsphandler.S,v 1.2 2025/06/08 18:55:35 christos Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: signalsphandler.S,v 1.2 2025/06/08 18:55:35 christos Exp $")
+
+/*
+ * signalsphandler(signo@x0)
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+ENTRY(signalsphandler)
+ mov x0, sp
+ adrp x1, _C_LABEL(signalsp)
+ str x0, [x1, :lo12:_C_LABEL(signalsp)]
+ ret
+END(signalsphandler)
diff --git a/kernel/arch/aarch64/stack_pointer.h b/kernel/arch/aarch64/stack_pointer.h
new file mode 100644
index 000000000000..3bd7e6f65335
--- /dev/null
+++ b/kernel/arch/aarch64/stack_pointer.h
@@ -0,0 +1,42 @@
+/* $NetBSD: stack_pointer.h,v 1.2 2025/04/20 22:31:00 riastradh Exp $ */
+
+/*
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TESTS_KERNEL_ARCH_AARCH64_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_AARCH64_STACK_POINTER_H
+
+#define MISALIGN_SP \
+ __asm__ volatile ( \
+ "sub sp, sp, #8" \
+ )
+
+#define FIX_SP \
+ __asm__ volatile ( \
+ "add sp, sp, #8" \
+ )
+
+#endif /* TESTS_KERNEL_ARCH_AARCH64_STACK_POINTER_H */
diff --git a/kernel/arch/aarch64/threadspfunc.S b/kernel/arch/aarch64/threadspfunc.S
new file mode 100644
index 000000000000..90f779d5e961
--- /dev/null
+++ b/kernel/arch/aarch64/threadspfunc.S
@@ -0,0 +1,43 @@
+/* $NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $")
+
+/*
+ * void *threadspfunc(void *cookie@x0)
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+ENTRY(threadspfunc)
+ mov x0, sp
+ ret
+END(threadspfunc)
diff --git a/kernel/arch/alpha/contextspfunc.S b/kernel/arch/alpha/contextspfunc.S
new file mode 100644
index 000000000000..da198f089124
--- /dev/null
+++ b/kernel/arch/alpha/contextspfunc.S
@@ -0,0 +1,50 @@
+/* $NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $")
+
+ .set noat
+ .text
+
+/*
+ * contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+LEAF(contextspfunc, 1)
+ LDGP(pv)
+ ldq at_reg, contextsp(gp) !literal
+ stq sp, 0(at_reg)
+ ldq pv, contextdone(gp) !literal
+ jmp (pv), contextdone
+END(contextspfunc)
diff --git a/kernel/arch/alpha/execsp.S b/kernel/arch/alpha/execsp.S
new file mode 100644
index 000000000000..08eaa521fa03
--- /dev/null
+++ b/kernel/arch/alpha/execsp.S
@@ -0,0 +1,106 @@
+/* $NetBSD: execsp.S,v 1.2 2025/04/21 02:31:22 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: execsp.S,v 1.2 2025/04/21 02:31:22 riastradh Exp $")
+
+ .set noat
+ .text
+
+/*
+ * void execsp_start(void *stackpointer@a0, void (*cleanup@a1)(void),
+ * void *obj_main@a2, struct ps_strings *ps_strings@a3)
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ */
+LEAF(execsp_start, 4)
+ LDGP(pv)
+ ldq at_reg, startsp(gp) !literal
+ stq sp, 0(at_reg)
+ ldq pv, __start(gp) !literal
+ jmp (pv), __start
+END(execsp_start)
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+LEAF(execsp_ctor, 0)
+ LDGP(pv)
+ ldq at_reg, ctorsp(gp) !literal
+ stq sp, 0(at_reg)
+ RET
+END(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .pushsection .ctors,"aw",@progbits
+ .p2align 3
+ .quad execsp_ctor
+ .popsection
+
+/*
+ * int main(int argc@a0, char **argv@a1, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+LEAF(main, 2)
+ LDGP(pv)
+ ldq at_reg, mainsp(gp) !literal
+ stq sp, 0(at_reg)
+ mov zero, v0
+ RET
+END(main)
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+LEAF(execsp_dtor, 0)
+ LDGP(pv)
+ ldq at_reg, dtorsp(gp) !literal
+ stq sp, 0(at_reg)
+ ldq pv, execsp_main(gp) !literal
+ jmp (pv), execsp_main
+END(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .pushsection .dtors,"aw",@progbits
+ .p2align 3
+ .quad execsp_dtor
+ .popsection
diff --git a/kernel/arch/alpha/signalsphandler.S b/kernel/arch/alpha/signalsphandler.S
new file mode 100644
index 000000000000..eaa3f2cdb654
--- /dev/null
+++ b/kernel/arch/alpha/signalsphandler.S
@@ -0,0 +1,49 @@
+/* $NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:31:57 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:31:57 riastradh Exp $")
+
+ .set noat
+ .text
+
+/*
+ * signalsphandler(signo@a0)
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+LEAF(signalsphandler, 1)
+ LDGP(pv)
+ ldq at_reg, signalsp(gp) !literal
+ stq sp, 0(at_reg)
+ RET
+END(signalsphandler)
diff --git a/kernel/arch/alpha/stack_pointer.h b/kernel/arch/alpha/stack_pointer.h
new file mode 100644
index 000000000000..58c581ffde0a
--- /dev/null
+++ b/kernel/arch/alpha/stack_pointer.h
@@ -0,0 +1,35 @@
+/* $NetBSD: stack_pointer.h,v 1.1 2025/04/20 22:31:57 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TESTS_KERNEL_ARCH_ALPHA_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_ALPHA_STACK_POINTER_H
+
+#define MISALIGN_SP __asm __volatile("lda $sp,-1($sp)" ::: "memory")
+#define FIX_SP __asm __volatile("lda $sp,1($sp)" ::: "memory")
+
+#endif /* TESTS_KERNEL_ARCH_ALPHA_STACK_POINTER_H */
diff --git a/kernel/arch/alpha/threadspfunc.S b/kernel/arch/alpha/threadspfunc.S
new file mode 100644
index 000000000000..413387bb0867
--- /dev/null
+++ b/kernel/arch/alpha/threadspfunc.S
@@ -0,0 +1,46 @@
+/* $NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $")
+
+ .set noat
+ .text
+
+/*
+ * void *threadspfunc(void *cookie@a0)
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+LEAF(threadspfunc, 1)
+ mov sp, v0
+ RET
+END(threadspfunc)
diff --git a/kernel/arch/arm/contextspfunc.S b/kernel/arch/arm/contextspfunc.S
new file mode 100644
index 000000000000..cf7fec19fb2a
--- /dev/null
+++ b/kernel/arch/arm/contextspfunc.S
@@ -0,0 +1,51 @@
+/* $NetBSD: contextspfunc.S,v 1.2 2025/05/07 16:26:47 uwe Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: contextspfunc.S,v 1.2 2025/05/07 16:26:47 uwe Exp $")
+
+/*
+ * contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+ENTRY(contextspfunc)
+0: GOT_INIT(r0, .Lgot)
+ mov r1, sp
+ GOT_GET(r2, r0, .Lcontextsp)
+ str r1, [r2]
+ b PLT_SYM(_C_LABEL(contextdone))
+
+ GOT_INITSYM(.Lgot, 0b)
+.Lcontextsp:
+ .word GOT_SYM(contextsp)
+END(contextspfunc)
diff --git a/kernel/arch/arm/execsp.S b/kernel/arch/arm/execsp.S
new file mode 100644
index 000000000000..b02587ef2b33
--- /dev/null
+++ b/kernel/arch/arm/execsp.S
@@ -0,0 +1,119 @@
+/* $NetBSD: execsp.S,v 1.4 2025/05/07 16:26:47 uwe Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: execsp.S,v 1.4 2025/05/07 16:26:47 uwe Exp $")
+
+/*
+ * void execsp_start(struct ps_strings *ps_strings@r0, void *obj_main@r1,
+ * void (*cleanup@r2)(void))
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ */
+ENTRY(execsp_start)
+0: GOT_INIT(r3, .Lgot.execsp_start)
+ mov r4, sp
+ GOT_GET(r5, r3, .Lstartsp)
+ str r4, [r5]
+ b PLT_SYM(_C_LABEL(__start))
+
+ GOT_INITSYM(.Lgot.execsp_start, 0b)
+.Lstartsp:
+ .word GOT_SYM(startsp)
+END(execsp_start)
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+ENTRY(execsp_ctor)
+0: GOT_INIT(r0, .Lgot.execsp_ctor)
+ mov r1, sp
+ GOT_GET(r2, r0, .Lctorsp)
+ str r1, [r2]
+ RET
+
+ GOT_INITSYM(.Lgot.execsp_ctor, 0b)
+.Lctorsp:
+ .word GOT_SYM(ctorsp)
+END(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .section .init_array,"aw",%init_array
+ .p2align 2
+ .word _C_LABEL(execsp_ctor)
+
+/*
+ * int main(int argc@r0, char **argv@r1, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+ENTRY(main)
+0: GOT_INIT(r0, .Lgot.main)
+ mov r1, sp
+ GOT_GET(r2, r0, .Lmainsp)
+ str r1, [r2]
+ mov r0, #0
+ RET
+
+ GOT_INITSYM(.Lgot.main, 0b)
+.Lmainsp:
+ .word GOT_SYM(mainsp)
+END(main)
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+ENTRY(execsp_dtor)
+0: GOT_INIT(r0, .Lgot.execsp_dtor)
+ mov r1, sp
+ GOT_GET(r2, r0, .Ldtorsp)
+ str r1, [r2]
+ b PLT_SYM(_C_LABEL(execsp_main))
+
+ GOT_INITSYM(.Lgot.execsp_dtor, 0b)
+.Ldtorsp:
+ .word GOT_SYM(dtorsp)
+END(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .section .fini_array,"aw",%fini_array
+ .p2align 2
+ .word _C_LABEL(execsp_dtor)
diff --git a/kernel/arch/arm/signalsphandler.S b/kernel/arch/arm/signalsphandler.S
new file mode 100644
index 000000000000..21b7d26a53b9
--- /dev/null
+++ b/kernel/arch/arm/signalsphandler.S
@@ -0,0 +1,51 @@
+/* $NetBSD: signalsphandler.S,v 1.3 2025/05/07 16:26:47 uwe Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: signalsphandler.S,v 1.3 2025/05/07 16:26:47 uwe Exp $")
+
+/*
+ * signalsphandler(signo@r0)
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+ENTRY(signalsphandler)
+0: GOT_INIT(r0, .Lgot)
+ mov r1, sp
+ GOT_GET(r2, r0, .Lsignalsp)
+ str r1, [r2]
+ RET
+
+ GOT_INITSYM(.Lgot, 0b)
+.Lsignalsp:
+ .word GOT_SYM(signalsp)
+END(signalsphandler)
diff --git a/kernel/arch/arm/stack_pointer.h b/kernel/arch/arm/stack_pointer.h
new file mode 100644
index 000000000000..73d10c341138
--- /dev/null
+++ b/kernel/arch/arm/stack_pointer.h
@@ -0,0 +1,35 @@
+/* $NetBSD: stack_pointer.h,v 1.1 2025/04/25 02:24:01 riastradh Exp $ */
+
+/*
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TESTS_KERNEL_ARCH_ARM_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_ARM_STACK_POINTER_H
+
+#define MISALIGN_SP __asm __volatile("sub sp, sp, #1")
+#define FIX_SP __asm __volatile("add sp, sp, #1")
+
+#endif /* TESTS_KERNEL_ARCH_ARM_STACK_POINTER_H */
diff --git a/kernel/arch/arm/threadspfunc.S b/kernel/arch/arm/threadspfunc.S
new file mode 100644
index 000000000000..5f34c547cadf
--- /dev/null
+++ b/kernel/arch/arm/threadspfunc.S
@@ -0,0 +1,43 @@
+/* $NetBSD: threadspfunc.S,v 1.1 2025/04/25 02:24:01 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: threadspfunc.S,v 1.1 2025/04/25 02:24:01 riastradh Exp $")
+
+/*
+ * void *threadspfunc(void *cookie@r0)
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+ENTRY(threadspfunc)
+ mov r0, sp
+ RET
+END(threadspfunc)
diff --git a/kernel/arch/hppa/contextspfunc.S b/kernel/arch/hppa/contextspfunc.S
new file mode 100644
index 000000000000..683f59808c95
--- /dev/null
+++ b/kernel/arch/hppa/contextspfunc.S
@@ -0,0 +1,48 @@
+/* $NetBSD: contextspfunc.S,v 1.2 2025/04/21 12:06:08 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: contextspfunc.S,v 1.2 2025/04/21 12:06:08 riastradh Exp $")
+
+/*
+ * contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+LEAF_ENTRY(contextspfunc)
+ addil LT%_C_LABEL(contextdone), %r19 /* r20 := contextdone */
+ ldw RT%_C_LABEL(contextdone)(%r1), %r20
+ addil LT%_C_LABEL(contextsp), %r19 /* r21 := &contextsp */
+ ldw RT%_C_LABEL(contextsp)(%r1), %r21
+ bv %r0(%r20) /* jump to contextdone */
+ stw %sp, 0(%r21) /* contextsp := sp */
+EXIT(contextspfunc)
diff --git a/kernel/arch/hppa/execregs.c b/kernel/arch/hppa/execregs.c
new file mode 100644
index 000000000000..c064d3a36e37
--- /dev/null
+++ b/kernel/arch/hppa/execregs.c
@@ -0,0 +1,387 @@
+/* $NetBSD: execregs.c,v 1.2 2025/02/28 16:08:19 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: execregs.c,v 1.2 2025/02/28 16:08:19 riastradh Exp $");
+
+#include "execregs.h"
+
+#include <errno.h>
+#include <spawn.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <unistd.h>
+
+extern char **environ;
+
+static unsigned long
+nonnull(unsigned long x)
+{
+
+ x |= x << 8;
+ x |= x << 16;
+ return x;
+}
+
+/*
+ * setfpregs()
+ *
+ * Set up all the floating-point registers with something nonzero
+ * in each one. We initialize the floating-point status register
+ * to set various bits so it's not all zero, but nothing that
+ * would trigger traps.
+ */
+static void
+setfpregs(void)
+{
+ static const uint64_t fpe[] = {
+ (__BITS(63,59) /* all exception flags (VZOUI) set */
+ | __BIT(58) /* C bit (comparison) set */
+ | __BITS(54,43) /* CQ (comparison queue) all set */
+ | __SHIFTIN(1, __BITS(42,41)) /* round toward zero */
+ | __SHIFTIN(0, __BIT(38)) /* no delayed trap */
+ | __SHIFTIN(1, __BIT(37)) /* Denormalized As Zero */
+ | __SHIFTIN(0, __BITS(36,32)) /* exceptions masked */
+ | 0x10101010),
+ 0x9191919111111111,
+ 0x9292929212121212,
+ 0x9393939313131313,
+ };
+ const uint64_t *fpep = fpe;
+
+ static const double fr[28] = {
+ 0x1.04p0, 0x1.05p0, 0x1.06p0, 0x1.07p0,
+ 0x1.08p0, 0x1.09p0, 0x1.0ap0, 0x1.0bp0,
+ 0x1.0cp0, 0x1.0dp0, 0x1.0ep0, 0x1.0fp0,
+ 0x1.10p0, 0x1.11p0, 0x1.12p0, 0x1.13p0,
+ 0x1.14p0, 0x1.15p0, 0x1.16p0, 0x1.17p0,
+ 0x1.18p0, 0x1.19p0, 0x1.1ap0, 0x1.1bp0,
+ 0x1.1cp0, 0x1.1dp0, 0x1.1ep0, 0x1.1fp0,
+ };
+ const double *frp = fr;
+
+ __asm volatile(
+ "fldds,ma 8(%0), %%fr0\n\t"
+ "fldds,ma 8(%0), %%fr1\n\t"
+ "fldds,ma 8(%0), %%fr2\n\t"
+ "fldds 0(%0), %%fr3"
+ : "+r"(fpep)
+ : "m"(fpe));
+
+ __asm volatile(
+ "fldds,ma 8(%0), %%fr4\n\t"
+ "fldds,ma 8(%0), %%fr5\n\t"
+ "fldds,ma 8(%0), %%fr6\n\t"
+ "fldds,ma 8(%0), %%fr7\n\t"
+ "fldds,ma 8(%0), %%fr8\n\t"
+ "fldds,ma 8(%0), %%fr9\n\t"
+ "fldds,ma 8(%0), %%fr10\n\t"
+ "fldds,ma 8(%0), %%fr11\n\t"
+ "fldds,ma 8(%0), %%fr12\n\t"
+ "fldds,ma 8(%0), %%fr13\n\t"
+ "fldds,ma 8(%0), %%fr14\n\t"
+ "fldds,ma 8(%0), %%fr15\n\t"
+ "fldds,ma 8(%0), %%fr16\n\t"
+ "fldds,ma 8(%0), %%fr17\n\t"
+ "fldds,ma 8(%0), %%fr18\n\t"
+ "fldds,ma 8(%0), %%fr19\n\t"
+ "fldds,ma 8(%0), %%fr20\n\t"
+ "fldds,ma 8(%0), %%fr21\n\t"
+ "fldds,ma 8(%0), %%fr22\n\t"
+ "fldds,ma 8(%0), %%fr23\n\t"
+ "fldds,ma 8(%0), %%fr24\n\t"
+ "fldds,ma 8(%0), %%fr25\n\t"
+ "fldds,ma 8(%0), %%fr26\n\t"
+ "fldds,ma 8(%0), %%fr27\n\t"
+ "fldds,ma 8(%0), %%fr28\n\t"
+ "fldds,ma 8(%0), %%fr29\n\t"
+ "fldds,ma 8(%0), %%fr30\n\t"
+ "fldds 0(%0), %%fr31"
+ : "+r"(frp)
+ : "m"(fr));
+}
+
+/*
+ * setpsw()
+ *
+ * Set some bits in PSW, the processor status word.
+ */
+static void
+setpsw(void)
+{
+ uint32_t x = 0xe0000000, y = 0xffffffff, sum;
+
+ /*
+ * Trigger some arithmetic that causes the carry/borrow
+ * (PSW[C/B]) bits to be set.
+ *
+ * XXX Also set PSW[V].
+ */
+ __asm volatile("sh3add %[sum], %[x], %[y]"
+ : /* outputs */ [sum] "=r"(sum)
+ : /* inputs */ [x] "r"(x), [y] "r"(y));
+}
+
+int
+execregschild(char *path)
+{
+ register long t1 __asm("r22") = nonnull(22);
+ register long t2 __asm("r21") = nonnull(21);
+ /* r30/sp: stack pointer */
+ register long t3 __asm("r20") = nonnull(20);
+ /* cr17/iisq_head: privileged */
+ /* cr17/iisq_tail: privileged */
+ /* cr18/iioq_head: privileged */
+ /* cr18/iioq_tail: privileged */
+ /* cr15/eiem: privileged */
+ /* cr22/ipsw: privileged */
+ /* sr3: privileged(?) */
+ /* cr8/pidr1: privileged */
+ /* cr20/isr: privileged */
+ /* cr21/ior: privileged */
+ /* cr19/iir: privileged */
+ /* flags: N/A(?) */
+ long sar = nonnull(0x8a); /* cr11 */
+ /* r1: ADDIL (add immediate left) result, nonnull anyway */
+ /* r2/rp: return pointer, nonnull anyway */
+ /* r3: frame pointer, nonnull anyway */
+ register long r4 __asm("r4") = nonnull(4);
+ register long r5 __asm("r5") = nonnull(5);
+ register long r6 __asm("r6") = nonnull(6);
+ register long r7 __asm("r7") = nonnull(7);
+ register long r8 __asm("r8") = nonnull(8);
+ register long r9 __asm("r9") = nonnull(9);
+ register long r10 __asm("r10") = nonnull(10);
+ register long r11 __asm("r11") = nonnull(11);
+ register long r12 __asm("r12") = nonnull(12);
+ register long r13 __asm("r13") = nonnull(13);
+ register long r14 __asm("r14") = nonnull(14);
+ register long r15 __asm("r15") = nonnull(15);
+ register long r16 __asm("r16") = nonnull(16);
+ register long r17 __asm("r17") = nonnull(17);
+ register long r18 __asm("r18") = nonnull(18);
+ register long t4 __asm("r19") = nonnull(19);
+ register long arg3 __asm("r23") = nonnull(23);
+ /* r24/arg2: envp, nonnull anyway */
+ /* r25/arg1: argv, nonnull anyway */
+ /* r26/arg0: path, nonnull anyway */
+ /* r27/dp: data pointer, nonnull anyway */
+ register long ret0 __asm("r28") = nonnull(28);
+ register long ret1 __asm("r29") = nonnull(29);
+ register long r31 __asm("r31") = nonnull(31);
+ /* sr0-sr7: space registers initialized by kernel */
+ /* cr9/pidr2: privileged */
+ /* cr12/pidr3: privileged */
+ /* cr13/pidr4: privileged */
+ /* cr0/rctr: privileged */
+ /* cr10/ccr: privileged */
+ /* cr23/eirr: privileged */
+ /* cr24: privileged */
+ /* cr25/vtop: privileged */
+ /* cr27/tr3: _lwp_private, thread-local storage -- nonnull anyway */
+ /* cr28: privileged */
+ /* cr30/fpregs: privileged */
+ /* cr31: privileged */
+
+ char *argv[] = {path, NULL};
+ char **envp = environ;
+
+ setfpregs();
+ setpsw();
+
+ /*
+ * Not perfect -- compiler might use some registers for
+ * stack/argument transfers, but all the arguments are nonnull
+ * so this is probably a good test anyway.
+ */
+ __asm volatile("mtctl %[sar], %%sar" /* cr11 */
+ : /* outputs */
+ : [sar] "r"(sar)
+ : "memory");
+ __asm volatile("" :
+ "+r"(t1),
+ "+r"(t2),
+ "+r"(t3),
+ "+r"(r4),
+ "+r"(r5),
+ "+r"(r6),
+ "+r"(r7),
+ "+r"(r8),
+ "+r"(r9),
+ "+r"(r10),
+ "+r"(r11),
+ "+r"(r12)
+ :: "memory");
+ /* pacify gcc error: more than 30 operands in 'asm' */
+ __asm volatile("" :
+ "+r"(r13),
+ "+r"(r14),
+ "+r"(r15),
+ "+r"(r16),
+ "+r"(r17),
+ "+r"(r18),
+ "+r"(t4),
+ "+r"(arg3),
+ "+r"(ret0),
+ "+r"(ret1),
+ "+r"(r31)
+ :: "memory");
+
+ return execve(path, argv, envp);
+}
+
+pid_t
+spawnregschild(char *path, int fd)
+{
+ register long t1 __asm("r22") = nonnull(22);
+ register long t2 __asm("r21") = nonnull(21);
+ /* r30/sp: stack pointer */
+ register long t3 __asm("r20") = nonnull(20);
+ /* cr17/iisq_head: privileged */
+ /* cr17/iisq_tail: privileged */
+ /* cr18/iioq_head: privileged */
+ /* cr18/iioq_tail: privileged */
+ /* cr15/eiem: privileged */
+ /* cr22/ipsw: privileged */
+ /* sr3: privileged(?) */
+ /* cr8/pidr1: privileged */
+ /* cr20/isr: privileged */
+ /* cr21/ior: privileged */
+ /* cr19/iir: privileged */
+ /* flags: N/A(?) */
+ long sar = nonnull(0x8a); /* cr11 */
+ /* r1: ADDIL (add immediate left) result, nonnull anyway */
+ /* r2/rp: return pointer, nonnull anyway */
+ /* r3: frame pointer, nonnull anyway */
+ register long r4 __asm("r4") = nonnull(4);
+ register long r5 __asm("r5") = nonnull(5);
+ register long r6 __asm("r6") = nonnull(6);
+ register long r7 __asm("r7") = nonnull(7);
+ register long r8 __asm("r8") = nonnull(8);
+ register long r9 __asm("r9") = nonnull(9);
+ register long r10 __asm("r10") = nonnull(10);
+ register long r11 __asm("r11") = nonnull(11);
+ register long r12 __asm("r12") = nonnull(12);
+ register long r13 __asm("r13") = nonnull(13);
+ register long r14 __asm("r14") = nonnull(14);
+ register long r15 __asm("r15") = nonnull(15);
+ register long r16 __asm("r16") = nonnull(16);
+ register long r17 __asm("r17") = nonnull(17);
+ register long r18 __asm("r18") = nonnull(18);
+ register long t4 __asm("r19") = nonnull(19);
+ /* r23/arg3: attrp, nonnull anyway */
+ /* r24/arg2: fileactsp, nonnull anyway */
+ /* r25/arg1: path, nonnull anyway */
+ /* r26/arg0: pidp, nonnull anyway */
+ /* r27/dp: data pointer, nonnull anyway */
+ register long ret0 __asm("r28") = nonnull(28);
+ register long ret1 __asm("r29") = nonnull(29);
+ register long r31 __asm("r31") = nonnull(31);
+ /* sr0-sr7: space registers initialized by kernel */
+ /* cr9/pidr2: privileged */
+ /* cr12/pidr3: privileged */
+ /* cr13/pidr4: privileged */
+ /* cr0/rctr: privileged */
+ /* cr10/ccr: privileged */
+ /* cr23/eirr: privileged */
+ /* cr24: privileged */
+ /* cr25/vtop: privileged */
+ /* cr27/tr3: _lwp_private, thread-local storage -- nonnull anyway */
+ /* cr28: privileged */
+ /* cr30/fpregs: privileged */
+ /* cr31: privileged */
+
+ char *argv[] = {path, NULL};
+ char **envp = environ;
+ posix_spawn_file_actions_t fileacts;
+ posix_spawnattr_t attr;
+ pid_t pid;
+ int error;
+
+ error = posix_spawn_file_actions_init(&fileacts);
+ if (error)
+ goto out;
+ error = posix_spawn_file_actions_adddup2(&fileacts, fd, STDOUT_FILENO);
+ if (error)
+ goto out;
+ error = posix_spawnattr_init(&attr);
+ if (error)
+ goto out;
+
+ setfpregs();
+ setpsw();
+
+ /*
+ * Not perfect -- compiler might use some registers for
+ * stack/argument transfers, but all the arguments are nonnull
+ * so this is probably a good test anyway.
+ */
+ __asm volatile("mtctl %[sar], %%sar" /* cr11 */
+ : /* outputs */
+ : [sar] "r"(sar)
+ : "memory");
+ __asm volatile("" :
+ "+r"(t1),
+ "+r"(t2),
+ "+r"(t3),
+ "+r"(r4),
+ "+r"(r5),
+ "+r"(r6),
+ "+r"(r7),
+ "+r"(r8),
+ "+r"(r9),
+ "+r"(r10),
+ "+r"(r11),
+ "+r"(r12)
+ :: "memory");
+ /* pacify gcc error: more than 30 operands in 'asm' */
+ __asm volatile("" :
+ "+r"(r13),
+ "+r"(r14),
+ "+r"(r15),
+ "+r"(r16),
+ "+r"(r17),
+ "+r"(r18),
+ "+r"(t4),
+ "+r"(ret0),
+ "+r"(ret1),
+ "+r"(r31)
+ :: "memory");
+
+ error = posix_spawn(&pid, path, &fileacts, &attr, argv, envp);
+ if (error)
+ goto out;
+
+out: posix_spawnattr_destroy(&attr);
+ posix_spawn_file_actions_destroy(&fileacts);
+ if (error) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/kernel/arch/hppa/execregs.h b/kernel/arch/hppa/execregs.h
new file mode 100644
index 000000000000..ae4dbb2e39c0
--- /dev/null
+++ b/kernel/arch/hppa/execregs.h
@@ -0,0 +1,157 @@
+/* $NetBSD: execregs.h,v 1.2 2025/02/28 16:08:19 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TESTS_KERNEL_ARCH_HPPA_EXECREGS_H
+#define TESTS_KERNEL_ARCH_HPPA_EXECREGS_H
+
+#include <sys/cdefs.h>
+
+#define NEXECREGS 96
+
+#ifndef _LOCORE
+
+#include <unistd.h>
+
+/*
+ * Ordered by struct struct trapframe in sys/arch/hppa/include/frame.h
+ * for convenience of auditing. Must match h_execregs.S.
+ */
+static const char *const regname[] = {
+ "t1",
+ "t2",
+ /* sp: stack pointer */
+ "t3",
+ /* various privileged stuff */
+ "sar",
+ "r1",
+ "rp",
+ /* r3: frame pointer (set to initial stack pointer) */
+ "r4",
+ "r5",
+ "r6",
+ "r70",
+ "r8",
+ "r9",
+ "r10",
+ "r11",
+ "r12",
+ "r13",
+ "r14",
+ "r15",
+ "r16",
+ "r17",
+ "r18",
+ "t4",
+ "arg3",
+ "arg2",
+ "arg1",
+ /* arg0: ps_strings */
+ "dp",
+ "ret0",
+ "ret1",
+ "r31",
+ "cr27",
+ "cr28",
+
+ "psw", /* user-visible PSW bits: C/B and V */
+
+ /* Floating-point registers */
+ "fr0l",
+ "fr0r",
+ "fr1l",
+ "fr1r",
+ "fr2l",
+ "fr2r",
+ "fr3l",
+ "fr3r",
+ "fr4l",
+ "fr4r",
+ "fr5l",
+ "fr5r",
+ "fr6l",
+ "fr6r",
+ "fr7l",
+ "fr7r",
+ "fr8l",
+ "fr8r",
+ "fr9l",
+ "fr9r",
+ "fr10l",
+ "fr10r",
+ "fr11l",
+ "fr11r",
+ "fr12l",
+ "fr12r",
+ "fr13l",
+ "fr13r",
+ "fr14l",
+ "fr14r",
+ "fr15l",
+ "fr15r",
+ "fr16l",
+ "fr16r",
+ "fr17l",
+ "fr17r",
+ "fr18l",
+ "fr18r",
+ "fr19l",
+ "fr19r",
+ "fr20l",
+ "fr20r",
+ "fr21l",
+ "fr21r",
+ "fr22l",
+ "fr22r",
+ "fr23l",
+ "fr23r",
+ "fr24l",
+ "fr24r",
+ "fr25l",
+ "fr25r",
+ "fr26l",
+ "fr26r",
+ "fr27l",
+ "fr27r",
+ "fr28l",
+ "fr28r",
+ "fr29l",
+ "fr29r",
+ "fr30l",
+ "fr30r",
+ "fr31l",
+ "fr31r",
+};
+
+__CTASSERT(NEXECREGS == __arraycount(regname));
+
+int execregschild(char *);
+pid_t spawnregschild(char *, int);
+
+#endif /* _LOCORE */
+
+#endif /* TESTS_KERNEL_ARCH_HPPA_EXECREGS_H */
diff --git a/kernel/arch/hppa/execsp.S b/kernel/arch/hppa/execsp.S
new file mode 100644
index 000000000000..520421a1f1fc
--- /dev/null
+++ b/kernel/arch/hppa/execsp.S
@@ -0,0 +1,130 @@
+/* $NetBSD: execsp.S,v 1.1 2025/04/20 22:32:49 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: execsp.S,v 1.1 2025/04/20 22:32:49 riastradh Exp $")
+
+ .import _GLOBAL_OFFSET_TABLE_
+
+/*
+ * void execsp_start(struct ps_strings *ps_strings@arg0,
+ * void (*cleanup@arg1)(void), void *obj_main@arg2)
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ */
+LEAF_ENTRY(execsp_start)
+ /*
+ * Set up the data pointer (r19) and linkage table register
+ * (r27) like the real startup routine so we can get at the
+ * global symbols startsp and __start.
+ *
+ * XXX Not really sure why we need to set up r27, since we only
+ * use r19 here and the real startup routine, __start, will set
+ * up both r19 and r27 anyway. But this crashes with SIGSEGV
+ * shortly after startup if we don't set up r27, and gdb
+ * crashes on my attempts to single-step, so I'll just leave
+ * the initialization of r27 here for now until someone is
+ * motivated by the potential for a single-instruction
+ * micro-optimization in this test program to find out why r27
+ * is needed too.
+ */
+ bl L$lpc, %r27
+ depi 0, 31, 2, %r27
+L$lpc: addil L'_GLOBAL_OFFSET_TABLE_ - ($PIC_pcrel$0 - 8), %r27
+ ldo R'_GLOBAL_OFFSET_TABLE_ - ($PIC_pcrel$0 - 12)(%r1), %r27
+ copy %r27, %r19
+
+ addil LT%_C_LABEL(startsp), %r19 /* r20 := &startsp */
+ ldw RT%_C_LABEL(startsp)(%r1), %r20
+
+ /* PIC_TAILCALL(__start), if we had it */
+ addil LT%_C_LABEL(__start), %r19 /* r1 := __start */
+ ldw RT%_C_LABEL(__start)(%r1), %r1
+ bv %r0(%r1) /* jump to __start */
+ stw %sp, 0(%r20) /* startsp := sp */
+EXIT(execsp_start)
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+LEAF_ENTRY(execsp_ctor)
+ addil LT%_C_LABEL(ctorsp), %r19 /* r1 := &ctorsp */
+ ldw RT%_C_LABEL(ctorsp)(%r1), %r1
+ bv %r0(%rp) /* return */
+ stw %sp, 0(%r1) /* ctorsp := sp */
+EXIT(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .section .ctors,"aw",@progbits
+ .p2align 2
+ .word _C_LABEL(execsp_ctor)
+
+/*
+ * int main(int argc@arg0, char **argv@arg1, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+LEAF_ENTRY(main)
+ addil LT%_C_LABEL(mainsp), %r19 /* r1 := &mainsp */
+ ldw RT%_C_LABEL(mainsp)(%r1), %r1
+ stw %sp, 0(%r1) /* ctorsp := sp */
+ bv %r0(%rp) /* return... */
+ copy %r0, %ret0 /* ...zero */
+EXIT(main)
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+LEAF_ENTRY(execsp_dtor)
+ addil LT%_C_LABEL(dtorsp), %r19 /* r20 := &dtorsp */
+ ldw RT%_C_LABEL(dtorsp)(%r1), %r20
+
+ /* PIC_TAILCALL(__start), if we had it */
+ addil LT%_C_LABEL(execsp_main), %r19 /* r1 := execsp_main */
+ ldw RT%_C_LABEL(execsp_main)(%r1), %r1
+ bv %r0(%r1) /* jump to execsp_main */
+ stw %sp, 0(%r20) /* startsp := sp */
+EXIT(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .section .dtors,"aw",@progbits
+ .p2align 2
+ .word _C_LABEL(execsp_dtor)
diff --git a/kernel/arch/hppa/h_execregs.S b/kernel/arch/hppa/h_execregs.S
new file mode 100644
index 000000000000..ad7a50a5b932
--- /dev/null
+++ b/kernel/arch/hppa/h_execregs.S
@@ -0,0 +1,168 @@
+/* $NetBSD: h_execregs.S,v 1.2 2025/02/28 16:08:19 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <sys/syscall.h>
+
+#include <machine/asm.h>
+#include <machine/vmparam.h>
+
+#include "execregs.h"
+
+_ENTRY(execregs_start)
+ .callinfo frame=(NEXECREGS*4), calls
+ .entry
+
+ ldo (NEXECREGS*4)(%sp), %sp /* space for NEXECREGS */
+ stw %t1, (4*(0 - NEXECREGS))(%sp) /* order matches execregs.h */
+ stw %t2, (4*(1 - NEXECREGS))(%sp)
+ /* sp: stack pointer */
+ stw %t3, (4*(2 - NEXECREGS))(%sp)
+ /* cr17/iisq_head: privileged */
+ /* cr17/iisq_tail: privileged */
+ /* cr18/iioq_head: privileged */
+ /* cr18/iioq_tail: privileged */
+ /* cr15/eiem: privileged */
+ /* cr22/ipsw: privileged */
+ /* sr3: privileged(?) */
+ /* cr8/pidr1: privileged */
+ /* cr20/isr: privileged */
+ /* cr21/ior: privileged */
+ /* cr19/iir: privileged */
+ /* flags: N/A(?) */
+ stw %sar, (4*(3 - NEXECREGS))(%sp)
+ stw %r1, (4*(4 - NEXECREGS))(%sp)
+ stw %rp, (4*(5 - NEXECREGS))(%sp)
+ /* r3: frame pointer (set to initial stack pointer) */
+ stw %r4, (4*(6 - NEXECREGS))(%sp)
+ stw %r5, (4*(7 - NEXECREGS))(%sp)
+ stw %r6, (4*(8 - NEXECREGS))(%sp)
+ stw %r7, (4*(9 - NEXECREGS))(%sp)
+ stw %r8, (4*(10 - NEXECREGS))(%sp)
+ stw %r9, (4*(11 - NEXECREGS))(%sp)
+ stw %r10, (4*(12 - NEXECREGS))(%sp)
+ stw %r11, (4*(13 - NEXECREGS))(%sp)
+ stw %r12, (4*(14 - NEXECREGS))(%sp)
+ stw %r13, (4*(15 - NEXECREGS))(%sp)
+ stw %r14, (4*(16 - NEXECREGS))(%sp)
+ stw %r15, (4*(17 - NEXECREGS))(%sp)
+ stw %r16, (4*(18 - NEXECREGS))(%sp)
+ stw %r17, (4*(19 - NEXECREGS))(%sp)
+ stw %r18, (4*(20 - NEXECREGS))(%sp)
+ stw %t4, (4*(21 - NEXECREGS))(%sp)
+ stw %arg3, (4*(22 - NEXECREGS))(%sp)
+ stw %arg2, (4*(23 - NEXECREGS))(%sp)
+ stw %arg1, (4*(24 - NEXECREGS))(%sp)
+ /* arg0: ps_strings */
+ stw %dp, (4*(25 - NEXECREGS))(%sp)
+ stw %ret0, (4*(26 - NEXECREGS))(%sp)
+ stw %ret1, (4*(27 - NEXECREGS))(%sp)
+ stw %r31, (4*(28 - NEXECREGS))(%sp)
+ /* sr0-sr7: space registers initialized by kernel */
+ /* cr9/pidr2: privileged */
+ /* cr12/pidr3: privileged */
+ /* cr13/pidr4: privileged */
+ /* cr0/rctr: privileged */
+ /* cr10/ccr: privileged */
+ /* cr23/eirr: privileged */
+ /* cr24: privileged */
+ /* cr25/vtop: privileged */
+ /* cr26: ??? */
+ stw %cr27, (4*(29 - NEXECREGS))(%sp)
+ stw %cr28, (4*(30 - NEXECREGS))(%sp)
+ /* cr30/fpregs: privileged */
+ /* cr31: privileged */
+
+ addc %t1, %r0, %r0 /* t1 := PSW[C/B]{0} */
+ zdep %t1, 23, 8, %t1 /* t1 := PSW */
+ stw %t1, (4*(31 - NEXECREGS))(%sp)
+
+ /* store the fp registers */
+ ldo (4*(32 - NEXECREGS))(%sp), %t1
+ fstd,ma %fr0, 8(%t1)
+ fstd,ma %fr1, 8(%t1)
+ fstd,ma %fr2, 8(%t1)
+ fstd,ma %fr3, 8(%t1)
+ fstd,ma %fr4, 8(%t1)
+ fstd,ma %fr5, 8(%t1)
+ fstd,ma %fr6, 8(%t1)
+ fstd,ma %fr7, 8(%t1)
+ fstd,ma %fr8, 8(%t1)
+ fstd,ma %fr9, 8(%t1)
+ fstd,ma %fr10, 8(%t1)
+ fstd,ma %fr11, 8(%t1)
+ fstd,ma %fr12, 8(%t1)
+ fstd,ma %fr13, 8(%t1)
+ fstd,ma %fr14, 8(%t1)
+ fstd,ma %fr15, 8(%t1)
+ fstd,ma %fr16, 8(%t1)
+ fstd,ma %fr17, 8(%t1)
+ fstd,ma %fr18, 8(%t1)
+ fstd,ma %fr19, 8(%t1)
+ fstd,ma %fr20, 8(%t1)
+ fstd,ma %fr21, 8(%t1)
+ fstd,ma %fr22, 8(%t1)
+ fstd,ma %fr23, 8(%t1)
+ fstd,ma %fr24, 8(%t1)
+ fstd,ma %fr25, 8(%t1)
+ fstd,ma %fr26, 8(%t1)
+ fstd,ma %fr27, 8(%t1)
+ fstd,ma %fr28, 8(%t1)
+ fstd,ma %fr29, 8(%t1)
+ fstd,ma %fr30, 8(%t1)
+ fstd %fr31, 0(%t1)
+
+ /* call write(STDOUT_FILENO, regs, sizeof(regs)) */
+ ldi 1, %arg0 /* arg0 := STDOUT_FILENO */
+ ldo -(4*NEXECREGS)(%sp), %arg1 /* arg1 := regs */
+ ldi (4*NEXECREGS), %arg2 /* arg2 := sizeof(regs) */
+ ldil L%SYSCALLGATE, %r1
+ ble 4(%sr2, %r1)
+ ldi SYS_write, %t1
+
+ comb,<>,n %r0, %t1, 2f /* bail if write failed */
+ ldi (4*NEXECREGS), %t1 /* bail if wrong # bytes */
+ comb,<>,n %ret0, %t1, 2f
+
+ /* call exit(0) */
+ ldi 0, %arg0
+1: ldil L%SYSCALLGATE, %r1
+ ble 4(%sr2, %r1)
+ ldi SYS_exit, %t1
+ break 0, 0 /* paranoia */
+
+2: /* call exit(127) */
+ b 1b
+ ldi 127, %arg0
+EXIT(execregs_start)
+
+/* main stub to simplify linking */
+LEAF_ENTRY(main)
+ break 0, 0 /* paranoia */
+EXIT(main)
diff --git a/kernel/arch/hppa/signalsphandler.S b/kernel/arch/hppa/signalsphandler.S
new file mode 100644
index 000000000000..01633d99ff8b
--- /dev/null
+++ b/kernel/arch/hppa/signalsphandler.S
@@ -0,0 +1,46 @@
+/* $NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:32:49 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:32:49 riastradh Exp $")
+
+/*
+ * signalsphandler(signo@arg0)
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+LEAF_ENTRY(signalsphandler)
+ addil LT%_C_LABEL(signalsp), %r19 /* r1 := &signalsp */
+ ldw RT%_C_LABEL(signalsp)(%r1), %r1
+ bv %r0(%rp) /* return */
+ stw %sp, 0(%r1) /* signalsp := sp */
+EXIT(signalsphandler)
diff --git a/kernel/arch/hppa/stack_pointer.h b/kernel/arch/hppa/stack_pointer.h
new file mode 100644
index 000000000000..acb73443621c
--- /dev/null
+++ b/kernel/arch/hppa/stack_pointer.h
@@ -0,0 +1,35 @@
+/* $NetBSD: stack_pointer.h,v 1.1 2025/04/20 22:32:49 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TESTS_KERNEL_ARCH_HPPA_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_HPPA_STACK_POINTER_H
+
+#define MISALIGN_SP __asm __volatile("ldo 1(%%sp),%%sp" ::: "memory")
+#define FIX_SP __asm __volatile("ldo -1(%%sp),%%sp" ::: "memory")
+
+#endif /* TESTS_KERNEL_ARCH_HPPA_STACK_POINTER_H */
diff --git a/kernel/arch/hppa/threadspfunc.S b/kernel/arch/hppa/threadspfunc.S
new file mode 100644
index 000000000000..f1b1d21fadfe
--- /dev/null
+++ b/kernel/arch/hppa/threadspfunc.S
@@ -0,0 +1,43 @@
+/* $NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $")
+
+/*
+ * void *threadspfunc(void *cookie@arg0)
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+LEAF_ENTRY(threadspfunc)
+ bv %r0(%rp) /* return to caller */
+ copy %sp, %ret0 /* return sp */
+EXIT(threadspfunc)
diff --git a/kernel/arch/i386/contextspfunc.S b/kernel/arch/i386/contextspfunc.S
new file mode 100644
index 000000000000..cc097cc61325
--- /dev/null
+++ b/kernel/arch/i386/contextspfunc.S
@@ -0,0 +1,56 @@
+/* $NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $")
+
+/*
+ * contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+ENTRY(contextspfunc)
+ call getpc_eax
+ addl $_GLOBAL_OFFSET_TABLE_,%eax
+ movl _C_LABEL(contextsp)@GOT(%eax),%eax
+ movl %esp,(%eax)
+ jmp _C_LABEL(contextdone)@PLT
+END(contextspfunc)
+
+ .text
+ _ALIGN_TEXT
+ .local getpc_eax
+ .type getpc_eax,@function
+getpc_eax:
+ movl (%esp),%eax
+ ret
+END(getpc_eax)
diff --git a/kernel/arch/i386/execregs.c b/kernel/arch/i386/execregs.c
new file mode 100644
index 000000000000..477cd82989e9
--- /dev/null
+++ b/kernel/arch/i386/execregs.c
@@ -0,0 +1,132 @@
+/* $NetBSD: execregs.c,v 1.1 2025/02/27 00:55:32 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: execregs.c,v 1.1 2025/02/27 00:55:32 riastradh Exp $");
+
+#include "execregs.h"
+
+#include <errno.h>
+#include <spawn.h>
+#include <stddef.h>
+#include <unistd.h>
+
+extern char **environ;
+
+static unsigned long
+nonnull(unsigned long x)
+{
+
+ x |= x << 8;
+ x |= x << 16;
+ return x;
+}
+
+int
+execregschild(char *path)
+{
+ register long edi __asm("edi") = nonnull('d');
+ register long esi __asm("esi") = nonnull('s');
+ /* ebp: frame pointer, can't touch that here, but it'll be nonnull */
+ /* ebx: ps_strings, passed to child */
+ register long edx __asm("edx") = nonnull('x');
+ register long ecx __asm("ecx") = nonnull('c');
+ register long eax __asm("eax") = nonnull('a');
+
+ char *argv[] = {path, NULL};
+ char **envp = environ;
+
+ /*
+ * Not perfect -- compiler might use some registers for
+ * stack/argument transfers, but all the arguments are nonnull
+ * so this is probably a good test anyway.
+ */
+ __asm volatile("" :
+ "+r"(edi),
+ "+r"(esi),
+ "+r"(edx),
+ "+r"(ecx),
+ "+r"(eax)
+ :: "memory");
+
+ return execve(path, argv, envp);
+}
+
+pid_t
+spawnregschild(char *path, int fd)
+{
+ register long edi __asm("edi") = nonnull('d');
+ register long esi __asm("esi") = nonnull('s');
+ /* ebp: frame pointer, can't touch that here, but it'll be nonnull */
+ /* ebx: ps_strings, passed to child */
+ register long edx __asm("edx") = nonnull('x');
+ register long ecx __asm("ecx") = nonnull('c');
+ register long eax __asm("eax") = nonnull('a');
+
+ char *argv[] = {path, NULL};
+ char **envp = environ;
+ posix_spawn_file_actions_t fileacts;
+ posix_spawnattr_t attr;
+ pid_t pid;
+ int error;
+
+ error = posix_spawn_file_actions_init(&fileacts);
+ if (error)
+ goto out;
+ error = posix_spawn_file_actions_adddup2(&fileacts, fd, STDOUT_FILENO);
+ if (error)
+ goto out;
+ error = posix_spawnattr_init(&attr);
+ if (error)
+ goto out;
+
+ /*
+ * Not perfect -- compiler might use some registers for
+ * stack/argument transfers, but all the arguments are nonnull
+ * so this is probably a good test anyway.
+ */
+ __asm volatile("" :
+ "+r"(edi),
+ "+r"(esi),
+ "+r"(edx),
+ "+r"(ecx),
+ "+r"(eax)
+ :: "memory");
+
+ error = posix_spawn(&pid, path, &fileacts, &attr, argv, envp);
+ if (error)
+ goto out;
+
+out: posix_spawnattr_destroy(&attr);
+ posix_spawn_file_actions_destroy(&fileacts);
+ if (error) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/kernel/arch/i386/execregs.h b/kernel/arch/i386/execregs.h
new file mode 100644
index 000000000000..f512bbabc1c7
--- /dev/null
+++ b/kernel/arch/i386/execregs.h
@@ -0,0 +1,69 @@
+/* $NetBSD: execregs.h,v 1.1 2025/02/27 00:55:32 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TESTS_KERNEL_ARCH_I386_EXECREGS_H
+#define TESTS_KERNEL_ARCH_I386_EXECREGS_H
+
+#include <sys/cdefs.h>
+
+#define NEXECREGS 6
+
+#ifndef _LOCORE
+
+#include <unistd.h>
+
+/*
+ * Ordered by struct trapframe in sys/arch/i386/include/frame.h for
+ * convenience of auditing. Must match h_execregs.S.
+ */
+static const char *const regname[] = {
+ /* gs/fs/es/ds: segment registers, not really registers */
+ "edi",
+ "esi",
+ "ebp",
+ /* ebx: ps_strings */
+ "edx",
+ "ecx",
+ "eax",
+ /* trapno: not a register */
+ /* err: not a register */
+ /* eip: instruction pointer */
+ /* cs: segment register */
+ /* eflags */
+ /* esp: stack pointer */
+ /* ss: stack selector */
+};
+
+__CTASSERT(NEXECREGS == __arraycount(regname));
+
+int execregschild(char *);
+pid_t spawnregschild(char *, int);
+
+#endif /* _LOCORE */
+
+#endif /* TESTS_KERNEL_ARCH_I386_EXECREGS_H */
diff --git a/kernel/arch/i386/execsp.S b/kernel/arch/i386/execsp.S
new file mode 100644
index 000000000000..133af37715ad
--- /dev/null
+++ b/kernel/arch/i386/execsp.S
@@ -0,0 +1,112 @@
+/* $NetBSD: execsp.S,v 1.1 2025/04/20 22:33:13 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: execsp.S,v 1.1 2025/04/20 22:33:13 riastradh Exp $")
+
+/*
+ * void execsp_start(void (*cleanup@edx)(void), void *obj_main@ecx,
+ * struct ps_strings *ps_strings@ebx)
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ */
+ENTRY(execsp_start)
+ call getpc_eax
+ addl $_GLOBAL_OFFSET_TABLE_,%eax
+ movl _C_LABEL(startsp)@GOT(%eax),%eax
+ movl %esp,(%eax)
+ jmp _C_LABEL(__start)@PLT
+END(execsp_start)
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+ENTRY(execsp_ctor)
+ call getpc_eax
+ addl $_GLOBAL_OFFSET_TABLE_,%eax
+ movl _C_LABEL(ctorsp)@GOT(%eax),%eax
+ movl %esp,(%eax)
+ ret
+END(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .section .ctors,"aw",@progbits
+ .p2align 2
+ .long _C_LABEL(execsp_ctor)
+
+/*
+ * int main(int argc@rdi, char **argv@rsi, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+ENTRY(main)
+ call getpc_eax
+ addl $_GLOBAL_OFFSET_TABLE_,%eax
+ movl _C_LABEL(mainsp)@GOT(%eax),%eax
+ movl %esp,(%eax)
+ xorl %eax,%eax
+ ret
+END(main)
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+ENTRY(execsp_dtor)
+ call getpc_eax
+ addl $_GLOBAL_OFFSET_TABLE_,%eax
+ movl _C_LABEL(dtorsp)@GOT(%eax),%eax
+ movl %esp,(%eax)
+ jmp _C_LABEL(execsp_main)@PLT
+END(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .section .dtors,"aw",@progbits
+ .p2align 2
+ .long _C_LABEL(execsp_dtor)
+
+ .text
+ _ALIGN_TEXT
+ .local getpc_eax
+ .type getpc_eax,@function
+getpc_eax:
+ movl (%esp),%eax
+ ret
+END(getpc_eax)
diff --git a/kernel/arch/i386/h_execregs.S b/kernel/arch/i386/h_execregs.S
new file mode 100644
index 000000000000..ac9b843f9d86
--- /dev/null
+++ b/kernel/arch/i386/h_execregs.S
@@ -0,0 +1,85 @@
+/* $NetBSD: h_execregs.S,v 1.1 2025/02/27 00:55:32 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <sys/syscall.h>
+
+#include <machine/asm.h>
+
+#include "execregs.h"
+
+ENTRY(execregs_start)
+ andl $-0x4,%esp /* align stack to 4-byte boundary */
+
+ /* store registers to a buffer on stack */
+ subl $(NEXECREGS*4),%esp /* space for NEXECREGS registers */
+ movl %edi,0*4(%esp) /* order matches execregs.h */
+ movl %esi,1*4(%esp)
+ movl %ebp,2*4(%esp)
+ movl %edx,3*4(%esp)
+ movl %ecx,4*4(%esp)
+ movl %eax,5*4(%esp)
+
+ /* call write(STDOUT_FILENO, regs, sizeof(regs)) */
+ movl %esp,%eax /* eax := regs */
+ pushl $(NEXECREGS*4) /* arg2 := sizeof(regs) */
+ pushl %eax /* arg1 := regs */
+ pushl $0x1 /* arg0 := STDOUT_FILENO */
+ call execregs_write
+
+ jb 2f /* bail if write failed */
+ cmpl $(NEXECREGS*4),%eax /* bail if wrote wrong # of bytes */
+ jne 2f
+
+ /* call exit(0) */
+ pushl $0 /* arg0 := 0 */
+1: call execregs_exit
+ hlt /* paranoia */
+
+2: /* call exit(127) */
+ pushl $127 /* arg0 := 127 */
+ jmp 1b
+END(execregs_start)
+
+ENTRY(execregs_write)
+ movl $SYS_write,%eax /* syscall number */
+ int $0x80
+ retl
+END(execregs_write)
+
+ENTRY(execregs_exit)
+ movl $SYS_exit,%eax /* syscall number */
+ int $0x80
+ hlt
+END(execregs_exit)
+
+/* main stub to simplify linking */
+ENTRY(main)
+ hlt
+END(main)
diff --git a/kernel/arch/i386/signalsphandler.S b/kernel/arch/i386/signalsphandler.S
new file mode 100644
index 000000000000..418825e78ab6
--- /dev/null
+++ b/kernel/arch/i386/signalsphandler.S
@@ -0,0 +1,56 @@
+/* $NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:33:13 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:33:13 riastradh Exp $")
+
+/*
+ * signalsphandler(signo@esp[4])
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+ENTRY(signalsphandler)
+ call getpc_eax
+ addl $_GLOBAL_OFFSET_TABLE_,%eax
+ movl _C_LABEL(signalsp)@GOT(%eax),%eax
+ movl %esp,(%eax)
+ ret
+END(signalsphandler)
+
+ .text
+ _ALIGN_TEXT
+ .local getpc_eax
+ .type getpc_eax,@function
+getpc_eax:
+ movl (%esp),%eax
+ ret
+END(getpc_eax)
diff --git a/kernel/arch/i386/stack_pointer.h b/kernel/arch/i386/stack_pointer.h
new file mode 100644
index 000000000000..6c95a28ab7b6
--- /dev/null
+++ b/kernel/arch/i386/stack_pointer.h
@@ -0,0 +1,35 @@
+/* $NetBSD: stack_pointer.h,v 1.1 2025/04/20 22:33:13 riastradh Exp $ */
+
+/*
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TESTS_KERNEL_ARCH_I386_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_I386_STACK_POINTER_H
+
+#define MISALIGN_SP __asm __volatile("addl $-1,%esp")
+#define FIX_SP __asm __volatile("addl $1,%esp")
+
+#endif /* TESTS_KERNEL_ARCH_I386_STACK_POINTER_H */
diff --git a/kernel/arch/i386/threadspfunc.S b/kernel/arch/i386/threadspfunc.S
new file mode 100644
index 000000000000..af03ff4c550e
--- /dev/null
+++ b/kernel/arch/i386/threadspfunc.S
@@ -0,0 +1,43 @@
+/* $NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $")
+
+/*
+ * void *threadspfunc(void *cookie@esp[4])
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+ENTRY(threadspfunc)
+ mov %esp,%eax
+ ret
+END(threadspfunc)
diff --git a/kernel/arch/mips/contextspfunc.S b/kernel/arch/mips/contextspfunc.S
new file mode 100644
index 000000000000..bee0a610dc86
--- /dev/null
+++ b/kernel/arch/mips/contextspfunc.S
@@ -0,0 +1,53 @@
+/* $NetBSD: contextspfunc.S,v 1.2 2025/04/27 16:49:54 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: contextspfunc.S,v 1.2 2025/04/27 16:49:54 riastradh Exp $")
+
+#include "SYS.h"
+
+ .text
+
+/*
+ * contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+LEAF(contextspfunc)
+ PIC_PROLOGUE(contextspfunc) /* gp setup, sets t3 */
+
+ PTR_LA t1, _C_LABEL(contextsp) /* load t1 := &contextsp */
+ NOP_L /* load hazard */
+ PTR_S sp, 0(t1) /* store contextsp := stack pointer */
+
+ PIC_TAILCALL(contextdone)
+END(contextspfunc)
diff --git a/kernel/arch/mips/execsp.S b/kernel/arch/mips/execsp.S
new file mode 100644
index 000000000000..b4f2dcfd6e2c
--- /dev/null
+++ b/kernel/arch/mips/execsp.S
@@ -0,0 +1,118 @@
+/* $NetBSD: execsp.S,v 1.2 2025/04/27 16:49:54 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: execsp.S,v 1.2 2025/04/27 16:49:54 riastradh Exp $")
+
+#include "SYS.h"
+
+ .text
+
+/*
+ * void execsp_start(void *stackpointer@a0, void (*cleanup@a1)(void),
+ * void *obj_main@a2, struct ps_strings *ps_strings@a3)
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ */
+LEAF(execsp_start)
+ PIC_PROLOGUE(execsp_start) /* gp setup, sets t3 */
+
+ PTR_LA t1, _C_LABEL(startsp) /* load t1 := &startsp */
+ NOP_L /* load hazard */
+ PTR_S sp, 0(t1) /* store startsp := stack pointer */
+
+ PIC_TAILCALL(__start) /* gp restore, uses t3 */
+END(execsp_start)
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+LEAF(execsp_ctor)
+ PIC_PROLOGUE(execsp_ctor) /* gp setup, sets t3 */
+
+ PTR_LA t1, _C_LABEL(ctorsp) /* load t1 := &ctorsp */
+ NOP_L /* load hazard */
+ PTR_S sp, 0(t1) /* store ctorsp := stack pointer */
+
+ PIC_RETURN() /* gp restore, uses t3 */
+END(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .pushsection .ctors,"aw",@progbits
+ .p2align PTR_SCALESHIFT
+ PTR_WORD _C_LABEL(execsp_ctor)
+ .popsection
+
+/*
+ * int main(int argc@a0, char **argv@a1, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+LEAF(main)
+ PIC_PROLOGUE(main) /* gp setup, sets t3 */
+
+ PTR_LA t1, _C_LABEL(mainsp) /* load t1 := &mainsp */
+ NOP_L /* load hazard */
+ PTR_S sp, 0(t1) /* store mainsp := stack pointer */
+
+ move v0, zero /* return 0 */
+
+ PIC_RETURN() /* gp restore, uses t3 */
+END(main)
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+LEAF(execsp_dtor)
+ PIC_PROLOGUE(execsp_dtor) /* gp setup, sets t3 */
+
+ PTR_LA t1, _C_LABEL(dtorsp) /* load t1 := &dtorsp */
+ NOP_L /* load hazard */
+ PTR_S sp, 0(t1) /* store dtorsp := stack pointer */
+
+ PIC_TAILCALL(execsp_main) /* gp restore, uses t3 */
+END(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .pushsection .dtors,"aw",@progbits
+ .p2align PTR_SCALESHIFT
+ PTR_WORD _C_LABEL(execsp_dtor)
+ .popsection
diff --git a/kernel/arch/mips/signalsphandler.S b/kernel/arch/mips/signalsphandler.S
new file mode 100644
index 000000000000..e742c2c95701
--- /dev/null
+++ b/kernel/arch/mips/signalsphandler.S
@@ -0,0 +1,53 @@
+/* $NetBSD: signalsphandler.S,v 1.2 2025/04/27 16:49:54 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: signalsphandler.S,v 1.2 2025/04/27 16:49:54 riastradh Exp $")
+
+#include "SYS.h"
+
+ .text
+
+/*
+ * signalsphandler(signo@a0)
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+LEAF(signalsphandler)
+ PIC_PROLOGUE(signalsphandler) /* gp setup, sets t3 */
+
+ PTR_LA t1, _C_LABEL(signalsp) /* load t1 := &signalsp */
+ NOP_L /* load hazard */
+ PTR_S sp, 0(t1) /* store signalsp := stack pointer */
+
+ PIC_RETURN()
+END(signalsphandler)
diff --git a/kernel/arch/mips/stack_pointer.h b/kernel/arch/mips/stack_pointer.h
new file mode 100644
index 000000000000..288c58928526
--- /dev/null
+++ b/kernel/arch/mips/stack_pointer.h
@@ -0,0 +1,40 @@
+/* $NetBSD: stack_pointer.h,v 1.2 2025/04/21 14:17:38 rin Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TESTS_KERNEL_ARCH_MIPS_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_MIPS_STACK_POINTER_H
+
+#ifdef __mips_o32
+#define MISALIGN_SP __asm __volatile("addiu $sp,$sp,-1" ::: "memory")
+#define FIX_SP __asm __volatile("addiu $sp,$sp,1" ::: "memory")
+#else
+#define MISALIGN_SP __asm __volatile("daddiu $sp,$sp,-1" ::: "memory")
+#define FIX_SP __asm __volatile("daddiu $sp,$sp,1" ::: "memory")
+#endif
+
+#endif /* TESTS_KERNEL_ARCH_MIPS_STACK_POINTER_H */
diff --git a/kernel/arch/mips/threadspfunc.S b/kernel/arch/mips/threadspfunc.S
new file mode 100644
index 000000000000..a42362fee67d
--- /dev/null
+++ b/kernel/arch/mips/threadspfunc.S
@@ -0,0 +1,46 @@
+/* $NetBSD: threadspfunc.S,v 1.2 2025/04/21 03:47:32 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: threadspfunc.S,v 1.2 2025/04/21 03:47:32 riastradh Exp $")
+
+ .text
+ .set noreorder
+
+/*
+ * void *threadspfunc(void *cookie@a0)
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+LEAF(threadspfunc)
+ j ra
+ move v0, sp
+END(threadspfunc)
diff --git a/kernel/arch/riscv/contextspfunc.S b/kernel/arch/riscv/contextspfunc.S
new file mode 100644
index 000000000000..7f405123deb3
--- /dev/null
+++ b/kernel/arch/riscv/contextspfunc.S
@@ -0,0 +1,45 @@
+/* $NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $")
+
+/*
+ * contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+ENTRY(contextspfunc)
+ PTR_LA t0, _C_LABEL(contextsp) /* t0 := &contextsp */
+ PTR_S sp, 0(t0) /* contextsp := sp */
+ tail _C_LABEL(contextdone) /* tail call contextdone */
+END(contextspfunc)
diff --git a/kernel/arch/riscv/execsp.S b/kernel/arch/riscv/execsp.S
new file mode 100644
index 000000000000..d045cbf0d3af
--- /dev/null
+++ b/kernel/arch/riscv/execsp.S
@@ -0,0 +1,100 @@
+/* $NetBSD: execsp.S,v 1.1 2025/04/20 22:34:07 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: execsp.S,v 1.1 2025/04/20 22:34:07 riastradh Exp $")
+
+/*
+ * void execsp_start(void (*cleanup@a0)(void),
+ * struct ps_strings *ps_strings@a1)
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ */
+ENTRY(execsp_start)
+ .option push
+ .option norelax /* Don't optimize this into `mv gp, gp'! */
+ lla gp, __global_pointer$
+ .option pop
+
+ PTR_LA t0, _C_LABEL(startsp) /* t0 := &startsp */
+ PTR_S sp, 0(t0) /* startsp := sp */
+ tail _C_LABEL(__start) /* tail call __start */
+END(execsp_start)
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+ENTRY(execsp_ctor)
+ PTR_LA t0, _C_LABEL(ctorsp) /* t0 := &ctorsp */
+ PTR_S sp, 0(t0) /* ctorsp := sp */
+ ret
+END(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .section .init_array,"aw"
+ .p2align PTR_SCALESHIFT
+ PTR_WORD _C_LABEL(execsp_ctor)
+
+/*
+ * int main(int argc@a0, char **argv@a1, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+ENTRY(main)
+ PTR_LA t0, _C_LABEL(mainsp) /* t0 := &mainsp */
+ PTR_S sp, 0(t0) /* mainsp := sp */
+ li a0, 0 /* return 0 */
+ ret
+END(main)
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+ENTRY(execsp_dtor)
+ PTR_LA t0, _C_LABEL(dtorsp) /* t0 := &dtorsp */
+ PTR_S sp, 0(t0) /* dtorsp := sp */
+ tail _C_LABEL(execsp_main) /* tail call execsp_main */
+END(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .section .fini_array,"aw"
+ .p2align PTR_SCALESHIFT
+ PTR_WORD _C_LABEL(execsp_dtor)
diff --git a/kernel/arch/riscv/signalsphandler.S b/kernel/arch/riscv/signalsphandler.S
new file mode 100644
index 000000000000..6a5d2f752ee2
--- /dev/null
+++ b/kernel/arch/riscv/signalsphandler.S
@@ -0,0 +1,45 @@
+/* $NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:34:07 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:34:07 riastradh Exp $")
+
+/*
+ * signalsphandler(signo@a0)
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+ENTRY(signalsphandler)
+ PTR_LA t0, _C_LABEL(signalsp) /* t0 := &signalsp */
+ PTR_S sp, 0(t0) /* signalsp := sp */
+ ret
+END(signalsphandler)
diff --git a/kernel/arch/riscv/stack_pointer.h b/kernel/arch/riscv/stack_pointer.h
new file mode 100644
index 000000000000..22c246e4a86a
--- /dev/null
+++ b/kernel/arch/riscv/stack_pointer.h
@@ -0,0 +1,35 @@
+/* $NetBSD: stack_pointer.h,v 1.1 2025/04/20 22:34:07 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TESTS_KERNEL_ARCH_RISCV_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_RISCV_STACK_POINTER_H
+
+#define MISALIGN_SP __asm __volatile("addi sp,sp,-1" ::: "memory")
+#define FIX_SP __asm __volatile("addi sp,sp,1" ::: "memory")
+
+#endif /* TESTS_KERNEL_ARCH_RISCV_STACK_POINTER_H */
diff --git a/kernel/arch/riscv/threadspfunc.S b/kernel/arch/riscv/threadspfunc.S
new file mode 100644
index 000000000000..30291ad2eb73
--- /dev/null
+++ b/kernel/arch/riscv/threadspfunc.S
@@ -0,0 +1,43 @@
+/* $NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $")
+
+/*
+ * void *threadspfunc(void *cookie@o0)
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+ENTRY(threadspfunc)
+ mv a0, sp /* return sp */
+ ret /* return to caller */
+END(threadspfunc)
diff --git a/kernel/arch/sh3/asm.h b/kernel/arch/sh3/asm.h
new file mode 100644
index 000000000000..16389b5c299e
--- /dev/null
+++ b/kernel/arch/sh3/asm.h
@@ -0,0 +1,58 @@
+/* $NetBSD: asm.h,v 1.1 2025/04/26 22:34:52 uwe Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TESTS_KERNEL_ARCH_SH3_ASM_H
+#define TESTS_KERNEL_ARCH_SH3_ASM_H
+
+#include <machine/asm.h>
+
+/*
+ * We define convenience macros to hide the difference between direct
+ * and PIC function calls, but with variable accesses it gets a bit
+ * more unwieldy, so don't impose this on the general audience
+ */
+
+#ifdef __PIC__
+
+#define VAR_DATUM(v) .long v@GOT
+
+#define MOVL_VAR(label, reg) \
+ mov.l label, r0; \
+ mov.l @(r0, r12), reg
+
+#else
+
+#define VAR_DATUM(v) .long v
+
+#define MOVL_VAR(label, reg) \
+ mov.l label, reg; \
+
+#endif
+
+
+#endif /* TESTS_KERNEL_ARCH_SH3_ASM_H */
diff --git a/kernel/arch/sh3/contextspfunc.S b/kernel/arch/sh3/contextspfunc.S
new file mode 100644
index 000000000000..51e91133417e
--- /dev/null
+++ b/kernel/arch/sh3/contextspfunc.S
@@ -0,0 +1,55 @@
+/* $NetBSD: contextspfunc.S,v 1.1 2025/04/26 22:34:52 uwe Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "asm.h"
+
+RCSID("$NetBSD: contextspfunc.S,v 1.1 2025/04/26 22:34:52 uwe Exp $")
+
+
+/*
+ * void contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+ENTRY(contextspfunc)
+ mov sp, r1
+ PIC_PROLOGUE(.L_GOT)
+
+ MOVL_VAR(.L_contextsp, r0)
+ mov.l r1, @r0
+
+ mov.l .L_contextdone, r0
+1: JUMP r0
+ PIC_EPILOGUE_SLOT
+
+ .p2align 2
+.L_GOT: PIC_GOT_DATUM
+.L_contextsp: VAR_DATUM(contextsp)
+.L_contextdone: CALL_DATUM(contextdone, 1b)
+ SET_ENTRY_SIZE(contextspfunc)
diff --git a/kernel/arch/sh3/execsp.S b/kernel/arch/sh3/execsp.S
new file mode 100644
index 000000000000..639ed4e6487b
--- /dev/null
+++ b/kernel/arch/sh3/execsp.S
@@ -0,0 +1,146 @@
+/* $NetBSD: execsp.S,v 1.2 2025/04/27 00:03:46 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "asm.h"
+
+RCSID("$NetBSD: execsp.S,v 1.2 2025/04/27 00:03:46 riastradh Exp $")
+
+
+/*
+ * void execsp_start()
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ *
+ * See sys/arch/sh3/sh3/sh3_machdep.c setregs()
+ */
+ENTRY(execsp_start)
+ mov sp, r1 // for consistency, don't need to
+ PIC_PROLOGUE_NOSAVE(.L_start_GOT)
+
+ MOVL_VAR(.L_startsp, r0)
+ mov.l r1, @r0
+
+ mov.l .L___start, r0
+1: JUMP r0
+ nop
+
+ .p2align 2
+.L_start_GOT: PIC_GOT_DATUM
+.L_startsp: VAR_DATUM(startsp)
+.L___start: CALL_DATUM(__start, 1b)
+ SET_ENTRY_SIZE(execsp_start)
+
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+ENTRY(execsp_ctor)
+ mov sp, r1
+ PIC_PROLOGUE(.L_ctor_GOT)
+
+ MOVL_VAR(.L_ctorsp, r0)
+ mov.l r1, @r0
+
+ rts
+ PIC_EPILOGUE_SLOT
+
+ .p2align 2
+.L_ctor_GOT: PIC_GOT_DATUM
+.L_ctorsp: VAR_DATUM(ctorsp)
+ SET_ENTRY_SIZE(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .pushsection .ctors, "aw", @progbits
+ .p2align 2
+ .long _C_LABEL(execsp_ctor)
+ .popsection
+
+
+/*
+ * int main(int argc, char **argv, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+ENTRY(main)
+ mov sp, r1
+ PIC_PROLOGUE(.L_main_GOT)
+
+ MOVL_VAR(.L_mainsp, r0)
+ mov.l r1, @r0
+
+ PIC_EPILOGUE
+ rts
+ mov #0, r0
+
+ .p2align 2
+.L_main_GOT: PIC_GOT_DATUM
+.L_mainsp: VAR_DATUM(mainsp)
+ SET_ENTRY_SIZE(main)
+
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+ENTRY(execsp_dtor)
+ mov sp, r1
+ sts.l pr, @-sp
+ PIC_PROLOGUE(.L_dtor_GOT)
+
+ MOVL_VAR(.L_dtorsp, r0)
+ mov.l r1, @r0
+
+ mov.l .L_execsp_main, r0
+1: CALL r0
+ nop
+
+ PIC_EPILOGUE
+ lds.l @sp+, pr
+ rts
+ nop
+
+ .p2align 2
+.L_dtor_GOT: PIC_GOT_DATUM
+.L_dtorsp: VAR_DATUM(dtorsp)
+.L_execsp_main: CALL_DATUM(execsp_main, 1b)
+ SET_ENTRY_SIZE(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .pushsection .dtors, "aw", @progbits
+ .p2align 2
+ .long _C_LABEL(execsp_dtor)
+ .popsection
diff --git a/kernel/arch/sh3/h_execregs.S b/kernel/arch/sh3/h_execregs.S
new file mode 100644
index 000000000000..d7c91161c34a
--- /dev/null
+++ b/kernel/arch/sh3/h_execregs.S
@@ -0,0 +1,86 @@
+/* $NetBSD: h_execregs.S,v 1.1 2025/04/27 02:24:07 uwe Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+#include <machine/asm.h>
+#include <machine/mcontext.h>
+#include <sys/syscall.h>
+
+
+// ELF entry point
+ENTRY(execregs_start)
+ // __gregset_t r; // mcontext (but general regs only)
+ mov.l sp, @-sp
+ mov.l r0, @-sp
+ mov.l r1, @-sp
+ mov.l r2, @-sp
+ mov.l r3, @-sp
+ mov.l r4, @-sp
+ mov.l r5, @-sp
+ mov.l r6, @-sp
+ mov.l r7, @-sp
+ mov.l r8, @-sp
+ mov.l r9, @-sp
+ mov.l r10, @-sp
+ mov.l r11, @-sp
+ mov.l r12, @-sp
+ mov.l r13, @-sp
+ mov.l r14, @-sp
+ sts.l pr, @-sp
+ sts.l mach, @-sp
+ sts.l macl, @-sp
+ mov #0, r0
+ mov.l r0, @-sp // _REG_SR is privileged
+ mova .Lend, r0 // _REG_PC
+ mov.l @r0, r1
+ sub r1, r0
+ mov.l r0, @-sp
+ stc.l gbr, @-sp
+
+ // write(STDOUT_FILENO, &r, sizeof(__gregset_t))
+ mov #1, r4
+ mov sp, r5
+ mov #(_NGREG * 4), r6
+ mov #SYS_write, r0
+ trapa #0x80
+
+ // _exit(0)
+ mov #0, r4
+ mov #SYS_exit, r0
+ trapa #0x80
+
+ .p2align 2
+.Lend: .long .Lend - execregs_start
+ SET_ENTRY_SIZE(execregs_start)
+
+
+// main stub to simplify linking
+ENTRY(main)
+ rts
+ mov #0, r0
+ SET_ENTRY_SIZE(main)
diff --git a/kernel/arch/sh3/signalsphandler.S b/kernel/arch/sh3/signalsphandler.S
new file mode 100644
index 000000000000..e0a86ae40558
--- /dev/null
+++ b/kernel/arch/sh3/signalsphandler.S
@@ -0,0 +1,61 @@
+/* $NetBSD: signalsphandler.S,v 1.2 2025/04/26 23:49:55 uwe Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "asm.h"
+RCSID("$NetBSD: signalsphandler.S,v 1.2 2025/04/26 23:49:55 uwe Exp $")
+
+
+/*
+ * void signalsphandler(int signo)
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+ENTRY(signalsphandler)
+ mov sp, r1
+#ifdef __PIC__
+ // PR kern/59327: don't touch stack as SP may be misaligned
+ // and as SuperH is a strict alignment architecture, we will
+ // get SIGBUS if we try to save registers on the stack
+ mov r12, r2
+#endif
+ PIC_PROLOGUE_NOSAVE(.L_GOT)
+
+ MOVL_VAR(.L_signalsp, r0)
+ mov.l r1, @r0
+
+#ifdef __PIC__
+ mov r2, r12
+#endif
+ rts
+ nop
+
+ .p2align 2
+.L_GOT: PIC_GOT_DATUM
+.L_signalsp: VAR_DATUM(signalsp)
+ SET_ENTRY_SIZE(signalsphandler)
diff --git a/kernel/arch/sh3/stack_pointer.h b/kernel/arch/sh3/stack_pointer.h
new file mode 100644
index 000000000000..ed3366602f56
--- /dev/null
+++ b/kernel/arch/sh3/stack_pointer.h
@@ -0,0 +1,35 @@
+/* $NetBSD: stack_pointer.h,v 1.1 2025/04/26 22:34:52 uwe Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TESTS_KERNEL_ARCH_SH3_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_SH3_STACK_POINTER_H
+
+#define MISALIGN_SP __asm __volatile("add #-1, sp" ::: "memory")
+#define FIX_SP __asm __volatile("add #+1, sp" ::: "memory")
+
+#endif /* TESTS_KERNEL_ARCH_SH3_STACK_POINTER_H */
diff --git a/kernel/arch/sh3/threadspfunc.S b/kernel/arch/sh3/threadspfunc.S
new file mode 100644
index 000000000000..de065e54191b
--- /dev/null
+++ b/kernel/arch/sh3/threadspfunc.S
@@ -0,0 +1,41 @@
+/* $NetBSD: threadspfunc.S,v 1.1 2025/04/26 22:34:52 uwe Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "asm.h"
+RCSID("$NetBSD: threadspfunc.S,v 1.1 2025/04/26 22:34:52 uwe Exp $")
+
+
+/*
+ * void *threadspfunc(void *cookie)
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+ENTRY(threadspfunc)
+ rts
+ mov sp, r0
+ SET_ENTRY_SIZE(threadspfunc)
diff --git a/kernel/arch/sparc/contextspfunc.S b/kernel/arch/sparc/contextspfunc.S
new file mode 100644
index 000000000000..a24185cabd73
--- /dev/null
+++ b/kernel/arch/sparc/contextspfunc.S
@@ -0,0 +1,64 @@
+/* $NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:45 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:45 riastradh Exp $")
+
+#ifdef __PIC__
+#define SETHI(label, got, reg) \
+ set label, reg; /* reg := &label - &GOT */ \
+ ld [got + reg], reg /* reg := &label */
+#define LO(label, reg) \
+ reg
+#else
+#define SETHI(label, got, reg) \
+ sethi %hi(label), reg /* reg := &label - %lo(label) */
+#define LO(label, reg) \
+ reg + %lo(label)
+#endif
+
+ .text
+
+/*
+ * contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+ENTRY(contextspfunc)
+ /* Reminder: o6 is frame pointer, o7 + 8 is return address. */
+ PIC_PROLOGUE(%g1, %o5) /* g1 := &GOT, clobber o5 */
+ SETHI(_C_LABEL(contextsp), %g1, %o5) /* o5 := &contextsp */
+ call _C_LABEL(contextdone) /* jump to contextdone */
+ st %sp, [LO(_C_LABEL(contextsp), %o5)] /* contextsp := sp */
+ /* don't care what happens here, caller must never return */
+ ta 1 /* Tcc, trap always */
+END(contextspfunc)
diff --git a/kernel/arch/sparc/execsp.S b/kernel/arch/sparc/execsp.S
new file mode 100644
index 000000000000..4b225dd6d550
--- /dev/null
+++ b/kernel/arch/sparc/execsp.S
@@ -0,0 +1,123 @@
+/* $NetBSD: execsp.S,v 1.1 2025/04/20 22:33:41 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: execsp.S,v 1.1 2025/04/20 22:33:41 riastradh Exp $")
+
+#ifdef __PIC__
+#define SETHI(label, got, reg) \
+ set label, reg; /* reg := &label - &GOT */ \
+ ld [got + reg], reg /* reg := &label */
+#define LO(label, reg) \
+ reg
+#else
+#define SETHI(label, got, reg) \
+ sethi %hi(label), reg /* reg := &label - %lo(label) */
+#define LO(label, reg) \
+ reg + %lo(label)
+#endif
+
+ .text
+
+/*
+ * void execsp_start(struct ps_strings *ps_strings@g1,
+ * void *obj_main@g2, void (*cleanup@g3)(void))
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ */
+ENTRY(execsp_start)
+ PIC_PROLOGUE(%o1, %o2) /* o1 := GOT, clobber o2 */
+ SETHI(_C_LABEL(startsp), %o1, %o2) /* o2 := &startup */
+ call _C_LABEL(__start) /* jump to start via PLT */
+ st %sp, [LO(_C_LABEL(startsp), %o2)] /* startsp := sp */
+ /* don't care what happens here, caller must never return */
+ ta 1 /* Tcc, trap always */
+END(execsp_start)
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+ENTRY(execsp_ctor)
+ /* Reminder: o6 is frame pointer, o7 + 8 is return address. */
+ PIC_PROLOGUE(%g1, %o5) /* g1 := &GOT, clobber o5 */
+ SETHI(_C_LABEL(ctorsp), %g1, %o5) /* o5 := &ctorsp */
+ retl /* return to caller */
+ st %sp, [LO(_C_LABEL(ctorsp), %o5)] /* ctorsp := sp */
+END(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .pushsection .ctors,"aw",@progbits
+ .p2align 2
+ .long _C_LABEL(execsp_ctor)
+ .popsection
+
+/*
+ * int main(int argc@a0, char **argv@a1, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+ENTRY(main)
+ /* Reminder: o6 is frame pointer, o7 + 8 is return address. */
+ PIC_PROLOGUE(%g1, %o5) /* g1 := &GOT, clobber o5 */
+ SETHI(_C_LABEL(mainsp), %g1, %o5) /* o5 := &mainsp */
+ st %sp, [LO(_C_LABEL(mainsp), %o5)] /* mainsp := sp */
+ retl /* return to caller */
+ mov 0, %o0 /* return 0 */
+END(main)
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+ENTRY(execsp_dtor)
+ /* Reminder: o6 is frame pointer, o7 + 8 is return address. */
+ PIC_PROLOGUE(%g1, %o5) /* g1 := &GOT, clobber o5 */
+ SETHI(_C_LABEL(dtorsp), %g1, %o5) /* o5 := &dtorsp - &GOT */
+ st %sp, [LO(_C_LABEL(dtorsp), %o5)] /* dtorsp := sp */
+ mov %o7, %o5 /* save return address */
+ call _C_LABEL(execsp_main) /* tail call to execsp_main */
+ mov %o5, %o7 /* restore return address */
+END(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .pushsection .dtors,"aw",@progbits
+ .p2align 2
+ .long _C_LABEL(execsp_dtor)
+ .popsection
diff --git a/kernel/arch/sparc/signalsphandler.S b/kernel/arch/sparc/signalsphandler.S
new file mode 100644
index 000000000000..187091d23c01
--- /dev/null
+++ b/kernel/arch/sparc/signalsphandler.S
@@ -0,0 +1,62 @@
+/* $NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:33:41 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:33:41 riastradh Exp $")
+
+#ifdef __PIC__
+#define SETHI(label, got, reg) \
+ set label, reg; /* reg := &label - &GOT */ \
+ ld [got + reg], reg /* reg := &label */
+#define LO(label, reg) \
+ reg
+#else
+#define SETHI(label, got, reg) \
+ sethi %hi(label), reg /* reg := &label - %lo(label) */
+#define LO(label, reg) \
+ reg + %lo(label)
+#endif
+
+ .text
+
+/*
+ * signalsphandler(signo@o0)
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+ENTRY(signalsphandler)
+ /* Reminder: o6 is frame pointer, o7 + 8 is return address. */
+ PIC_PROLOGUE(%g1, %o5) /* g1 := &GOT, clobber o5 */
+ SETHI(_C_LABEL(signalsp), %g1, %o5) /* o5 := &signalsp */
+ retl /* return to caller */
+ st %sp, [LO(_C_LABEL(signalsp), %o5)] /* signalsp := sp */
+END(signalsphandler)
diff --git a/kernel/arch/sparc/stack_pointer.h b/kernel/arch/sparc/stack_pointer.h
new file mode 100644
index 000000000000..20c06bec77cd
--- /dev/null
+++ b/kernel/arch/sparc/stack_pointer.h
@@ -0,0 +1,35 @@
+/* $NetBSD: stack_pointer.h,v 1.1 2025/04/20 22:33:41 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TESTS_KERNEL_ARCH_SPARC_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_SPARC_STACK_POINTER_H
+
+#define MISALIGN_SP __asm __volatile("dec %%sp" ::: "memory")
+#define FIX_SP __asm __volatile("inc %%sp" ::: "memory")
+
+#endif /* TESTS_KERNEL_ARCH_SPARC_STACK_POINTER_H */
diff --git a/kernel/arch/sparc/threadspfunc.S b/kernel/arch/sparc/threadspfunc.S
new file mode 100644
index 000000000000..794804d85741
--- /dev/null
+++ b/kernel/arch/sparc/threadspfunc.S
@@ -0,0 +1,45 @@
+/* $NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:45 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:45 riastradh Exp $")
+
+ .text
+
+/*
+ * void *threadspfunc(void *cookie@o0)
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+ENTRY(threadspfunc)
+ retl /* return to caller */
+ mov %sp, %o0 /* return sp */
+END(threadspfunc)
diff --git a/kernel/arch/vax/execregs.c b/kernel/arch/vax/execregs.c
new file mode 100644
index 000000000000..414b810336d8
--- /dev/null
+++ b/kernel/arch/vax/execregs.c
@@ -0,0 +1,166 @@
+/* $NetBSD: execregs.c,v 1.1 2025/02/27 00:55:32 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: execregs.c,v 1.1 2025/02/27 00:55:32 riastradh Exp $");
+
+#include "execregs.h"
+
+#include <errno.h>
+#include <spawn.h>
+#include <stddef.h>
+#include <unistd.h>
+
+extern char **environ;
+
+static unsigned long
+nonnull(unsigned long x)
+{
+
+ x |= x << 8;
+ x |= x << 16;
+ return x;
+}
+
+int
+execregschild(char *path)
+{
+ /* fp: frame pointer, nonnull */
+ /* ap: argument pointer, on user stack, nonnull */
+ /* sp: stack pointer, nonnull */
+ register long r0 __asm("r0") = nonnull(0x10);
+ register long r1 __asm("r1") = nonnull(1);
+ register long r2 __asm("r2") = nonnull(2);
+ register long r3 __asm("r3") = nonnull(3);
+ register long r4 __asm("r4") = nonnull(4);
+ register long r5 __asm("r5") = nonnull(5);
+ register long r6 __asm("r6") = nonnull(6);
+ register long r7 __asm("r7") = nonnull(7);
+ register long r8 __asm("r8") = nonnull(8);
+ register long r9 __asm("r9") = nonnull(9);
+ register long r10 __asm("r10") = nonnull(10);
+ register long r11 __asm("r11") = nonnull(11);
+ /* pc: user PC, will be nonnull */
+ /* psl: processor status longword, will be nonnull */
+
+ char *argv[] = {path, NULL};
+ char **envp = environ;
+
+ /*
+ * Not perfect -- compiler might use some registers for
+ * stack/argument transfers, but all the arguments are nonnull
+ * so this is probably a good test anyway.
+ */
+ __asm volatile("" :
+ "+r"(r0),
+ "+r"(r1),
+ "+r"(r2),
+ "+r"(r3),
+ "+r"(r4),
+ "+r"(r5),
+ "+r"(r6),
+ "+r"(r7),
+ "+r"(r8),
+ "+r"(r9),
+ "+r"(r10),
+ "+r"(r11)
+ :: "memory");
+
+ return execve(path, argv, envp);
+}
+
+pid_t
+spawnregschild(char *path, int fd)
+{
+ /* fp: frame pointer, nonnull */
+ /* ap: argument pointer, on user stack, nonnull */
+ /* sp: stack pointer, nonnull */
+ register long r0 __asm("r0") = nonnull(0x10);
+ register long r1 __asm("r1") = nonnull(1);
+ register long r2 __asm("r2") = nonnull(2);
+ register long r3 __asm("r3") = nonnull(3);
+ register long r4 __asm("r4") = nonnull(4);
+ register long r5 __asm("r5") = nonnull(5);
+ register long r6 __asm("r6") = nonnull(6);
+ register long r7 __asm("r7") = nonnull(7);
+ register long r8 __asm("r8") = nonnull(8);
+ register long r9 __asm("r9") = nonnull(9);
+ register long r10 __asm("r10") = nonnull(10);
+ register long r11 __asm("r11") = nonnull(11);
+ /* pc: user PC, will be nonnull */
+ /* psl: processor status longword, will be nonnull */
+
+ char *argv[] = {path, NULL};
+ char **envp = environ;
+ posix_spawn_file_actions_t fileacts;
+ posix_spawnattr_t attr;
+ pid_t pid;
+ int error;
+
+ error = posix_spawn_file_actions_init(&fileacts);
+ if (error)
+ goto out;
+ error = posix_spawn_file_actions_adddup2(&fileacts, fd, STDOUT_FILENO);
+ if (error)
+ goto out;
+ error = posix_spawnattr_init(&attr);
+ if (error)
+ goto out;
+
+ /*
+ * Not perfect -- compiler might use some registers for
+ * stack/argument transfers, but all the arguments are nonnull
+ * so this is probably a good test anyway.
+ */
+ __asm volatile("" :
+ "+r"(r0),
+ "+r"(r1),
+ "+r"(r2),
+ "+r"(r3),
+ "+r"(r4),
+ "+r"(r5),
+ "+r"(r6),
+ "+r"(r7),
+ "+r"(r8),
+ "+r"(r9),
+ "+r"(r10),
+ "+r"(r11)
+ :: "memory");
+
+ error = posix_spawn(&pid, path, &fileacts, &attr, argv, envp);
+ if (error)
+ goto out;
+
+out: posix_spawnattr_destroy(&attr);
+ posix_spawn_file_actions_destroy(&fileacts);
+ if (error) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/kernel/arch/vax/execregs.h b/kernel/arch/vax/execregs.h
new file mode 100644
index 000000000000..882bcd1b6702
--- /dev/null
+++ b/kernel/arch/vax/execregs.h
@@ -0,0 +1,77 @@
+/* $NetBSD: execregs.h,v 1.1 2025/02/27 00:55:32 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TESTS_KERNEL_ARCH_VAX_EXECREGS_H
+#define TESTS_KERNEL_ARCH_VAX_EXECREGS_H
+
+#include <sys/cdefs.h>
+
+#define NEXECREGS 12
+
+#ifndef _LOCORE
+
+#include <unistd.h>
+
+/*
+ * The order matches that in struct trapframe in
+ * sys/arch/vax/include/trap.h
+ *
+ * Must match h_execregs.S.
+ *
+ * See also sys/arch/vax/vax/trap.c:setregs()
+ */
+static const char *const regname[] = {
+ "fp", /* Stack frame pointer */
+ "ap", /* Argument pointer on user stack */
+ /* sp: stack pointer */
+ "r0", /* General registers saved upon trap/syscall */
+ "r1",
+ "r2",
+ "r3",
+ "r4",
+ "r5",
+ /* r6: initial stack pointer */
+ "r7",
+ "r8",
+ /* r9: ps_strings */
+ "r10",
+ "r11",
+ /* trap: type of trap, not a register */
+ /* code: trap specific code, not a register */
+ /* pc: user PC */
+ /* psl: processor status longword */
+};
+
+__CTASSERT(NEXECREGS == __arraycount(regname));
+
+int execregschild(char *);
+pid_t spawnregschild(char *, int);
+
+#endif /* _LOCORE */
+
+#endif /* TESTS_KERNEL_ARCH_VAX_EXECREGS_H */
diff --git a/kernel/arch/vax/h_execregs.S b/kernel/arch/vax/h_execregs.S
new file mode 100644
index 000000000000..27c5c2052867
--- /dev/null
+++ b/kernel/arch/vax/h_execregs.S
@@ -0,0 +1,87 @@
+/* $NetBSD: h_execregs.S,v 1.1 2025/02/27 00:55:32 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <sys/syscall.h>
+
+#include <machine/asm.h>
+
+#include "execregs.h"
+
+#define REGSIZE 4
+#define BUFSIZE (NEXECREGS * REGSIZE)
+#define SLOT(n) (n)*REGSIZE(%sp)
+
+ENTRY(execregs_start, 0)
+ /* store registers to a buffer on stack */
+ subl2 $BUFSIZE,%sp /* space for NEXECREGS registers */
+ movl %fp,SLOT(0) /* order matches execregs.h */
+ movl %ap,SLOT(1)
+ /* sp: stack pointer */
+ movl %r0,SLOT(2)
+ movl %r1,SLOT(3)
+ movl %r2,SLOT(4)
+ movl %r3,SLOT(5)
+ movl %r4,SLOT(6)
+ movl %r5,SLOT(7)
+ /* r6: initial stack pointer */
+ movl %r7,SLOT(8)
+ movl %r8,SLOT(9)
+ /* r9: ps_strings */
+ movl %r10,SLOT(10)
+ movl %r11,SLOT(11)
+
+ /* call write(STDOUT_FILENO, regs, sizeof(regs)) */
+ pushl $BUFSIZE /* arg2 := sizeof(regs) */
+ pushal 4(%sp) /* arg1 := regs */
+ pushl $1 /* arg0 := STDOUT_FILENO */
+ pushl $3 /* number of arguments */
+ movl %sp,%ap /* argument pointer */
+ chmk $SYS_write
+
+ bcs 2f /* bail if write failed */
+ cmpl $BUFSIZE,%r0 /* bail if wrote wrong # of bytes */
+ bneq 2f
+
+ /* call exit(0) */
+ pushl $0 /* arg0 := 0 */
+1: pushl $1 /* number of arguments */
+ movl %sp,%ap /* argument pointer */
+ chmk $SYS_exit
+ .word 0xffff /* paranoia -- illegal opcode */
+
+2: /* call exit(127) */
+ pushl $127 /* arg0 := 127 */
+ jmp 1b
+END(execregs_start)
+
+/* main stub to simplify linking */
+ENTRY(main, 0)
+ .word 0xffff /* illegal opcode */
+END(main)
diff --git a/kernel/arch/x86_64/contextspfunc.S b/kernel/arch/x86_64/contextspfunc.S
new file mode 100644
index 000000000000..d01e1dcdcaf3
--- /dev/null
+++ b/kernel/arch/x86_64/contextspfunc.S
@@ -0,0 +1,55 @@
+/* $NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:45 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:45 riastradh Exp $")
+
+/*
+ * contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+ENTRY(contextspfunc)
+ /*
+ * `The end of the input argument area shall be aligned on a
+ * [16-byte] boundary. In other words, the value of (%rsp + 8)
+ * is always a multiple of 16 when control is transferred to
+ * the function entry point.'
+ *
+ * To make it convenient for t_signal_and_sp.c, we subtract 8
+ * from %rsp in order to get something congruent to zero modulo
+ * the stack alignemnt.
+ */
+ movq %rsp,_C_LABEL(contextsp)(%rip)
+ addq $-8,_C_LABEL(contextsp)(%rip)
+ call _C_LABEL(contextdone)
+END(contextspfunc)
diff --git a/kernel/arch/x86_64/execregs.c b/kernel/arch/x86_64/execregs.c
new file mode 100644
index 000000000000..fad1b40daa5e
--- /dev/null
+++ b/kernel/arch/x86_64/execregs.c
@@ -0,0 +1,156 @@
+/* $NetBSD: execregs.c,v 1.1 2025/02/27 00:55:32 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: execregs.c,v 1.1 2025/02/27 00:55:32 riastradh Exp $");
+
+#include "execregs.h"
+
+#include <errno.h>
+#include <spawn.h>
+#include <stddef.h>
+#include <unistd.h>
+
+extern char **environ;
+
+static unsigned long
+nonnull(unsigned long x)
+{
+
+ x |= x << 8;
+ x |= x << 16;
+ x |= x << 32;
+ return x;
+}
+
+int
+execregschild(char *path)
+{
+ /* rdi: used to pass exec arg0, nonnull anyway (path) */
+ /* rsi: used to pass exec arg1, nonnull anyway (argv) */
+ /* rdx: used to pass exec arg2, nonnull anyway (environ) */
+ register long r10 __asm("r10") = nonnull(10);
+ register long r8 __asm("r8") = nonnull(8);
+ register long r9 __asm("r9") = nonnull(9);
+ register long rcx __asm("rcx") = nonnull('c');
+ register long r11 __asm("r11") = nonnull(11);
+ register long r12 __asm("r12") = nonnull(12);
+ register long r13 __asm("r13") = nonnull(13);
+ register long r14 __asm("r14") = nonnull(14);
+ register long r15 __asm("r15") = nonnull(15);
+ /* rbp: frame pointer, can't touch that here, but it'll be nonnull */
+ /* rbx: ps_strings, passed to child */
+ register long rax __asm("rax") = nonnull('a');
+
+ char *argv[] = {path, NULL};
+ char **envp = environ;
+
+ /*
+ * Not perfect -- compiler might use some registers for
+ * stack/argument transfers, but all the arguments are nonnull
+ * so this is probably a good test anyway.
+ */
+ __asm volatile("" :
+ "+r"(r10),
+ "+r"(r8),
+ "+r"(r9),
+ "+r"(rcx),
+ "+r"(r11),
+ "+r"(r12),
+ "+r"(r13),
+ "+r"(r14),
+ "+r"(r15),
+ "+r"(rax)
+ :: "memory");
+
+ return execve(path, argv, envp);
+}
+
+pid_t
+spawnregschild(char *path, int fd)
+{
+ /* rdi: used to pass posix_spawn arg0, nonnull anyway (&pid) */
+ /* rsi: used to pass posix_spawn arg1, nonnull anyway (path) */
+ /* rdx: used to pass posix_spawn arg2, nonnull anyway (&fileacts) */
+ register long r10 __asm("r10") = nonnull(10);
+ /* r8: used to pass posix_spawn arg4, nonnull anyway (argv) */
+ /* r9: used to pass posix_spawn arg5, nonnull anyway (environ) */
+ /* rcx: used to pass posix_spawn arg3, nonnull anyway (&attr) */
+ register long r11 __asm("r11") = nonnull(11);
+ register long r12 __asm("r12") = nonnull(12);
+ register long r13 __asm("r13") = nonnull(13);
+ register long r14 __asm("r14") = nonnull(14);
+ register long r15 __asm("r15") = nonnull(15);
+ /* rbp: frame pointer, can't touch that here, but it'll be nonnull */
+ /* rbx: ps_strings, passed to child */
+ register long rax __asm("rax") = nonnull('a');
+
+ char *argv[] = {path, NULL};
+ char **envp = environ;
+ posix_spawn_file_actions_t fileacts;
+ posix_spawnattr_t attr;
+ pid_t pid;
+ int error;
+
+ error = posix_spawn_file_actions_init(&fileacts);
+ if (error)
+ goto out;
+ error = posix_spawn_file_actions_adddup2(&fileacts, fd, STDOUT_FILENO);
+ if (error)
+ goto out;
+ error = posix_spawnattr_init(&attr);
+ if (error)
+ goto out;
+
+ /*
+ * Not perfect -- compiler might use some registers for
+ * stack/argument transfers, but all the arguments are nonnull
+ * so this is probably a good test anyway.
+ */
+ __asm volatile("" :
+ "+r"(r10),
+ "+r"(r11),
+ "+r"(r12),
+ "+r"(r13),
+ "+r"(r14),
+ "+r"(r15),
+ "+r"(rax)
+ :: "memory");
+
+ error = posix_spawn(&pid, path, &fileacts, &attr, argv, envp);
+ if (error)
+ goto out;
+
+out: posix_spawnattr_destroy(&attr);
+ posix_spawn_file_actions_destroy(&fileacts);
+ if (error) {
+ errno = error;
+ return -1;
+ }
+ return 0;
+}
diff --git a/kernel/arch/x86_64/execregs.h b/kernel/arch/x86_64/execregs.h
new file mode 100644
index 000000000000..904991777483
--- /dev/null
+++ b/kernel/arch/x86_64/execregs.h
@@ -0,0 +1,81 @@
+/* $NetBSD: execregs.h,v 1.1 2025/02/27 00:55:32 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TESTS_KERNEL_ARCH_X86_64_EXECREGS_H
+#define TESTS_KERNEL_ARCH_X86_64_EXECREGS_H
+
+#include <sys/cdefs.h>
+
+#define NEXECREGS 14
+
+#ifndef _LOCORE
+
+#include <unistd.h>
+
+/*
+ * Ordered by _FRAME_REG in sys/arch/amd64/include/frame_regs.h for
+ * convenience of auditing. Must match h_execregs.S.
+ */
+static const char *const regname[] = {
+ "rdi",
+ "rsi",
+ "rdx",
+ "r10",
+ "r8",
+ "r9",
+ /* arg6: syscall arg from stack, not a real register */
+ /* arg7: syscall arg from stack, not a real register */
+ /* arg8: syscall arg from stack, not a real register */
+ /* arg9: syscall arg from stack, not a real register */
+ "rcx",
+ "r11",
+ "r12",
+ "r13",
+ "r14",
+ "r15",
+ "rbp",
+ /* rbx: ps_strings */
+ "rax",
+ /* gs/fs/es/ds: segment registers, not really registers */
+ /* trapno: not a register */
+ /* err: not a register */
+ /* rip: instruction pointer */
+ /* cs: segment register */
+ /* rflags */
+ /* rsp: stack pointer */
+ /* ss: stack selector */
+};
+
+__CTASSERT(NEXECREGS == __arraycount(regname));
+
+int execregschild(char *);
+pid_t spawnregschild(char *, int);
+
+#endif /* _LOCORE */
+
+#endif /* TESTS_KERNEL_ARCH_X86_64_EXECREGS_H */
diff --git a/kernel/arch/x86_64/execsp.S b/kernel/arch/x86_64/execsp.S
new file mode 100644
index 000000000000..d351dfb2d9e8
--- /dev/null
+++ b/kernel/arch/x86_64/execsp.S
@@ -0,0 +1,111 @@
+/* $NetBSD: execsp.S,v 1.2 2025/04/20 22:31:25 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: execsp.S,v 1.2 2025/04/20 22:31:25 riastradh Exp $")
+
+/*
+ * void execsp_start(void (*cleanup@rbx)(void), void *obj_main@rcx,
+ * struct ps_strings *ps_strings@rbx)
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ */
+ENTRY(execsp_start)
+ movq %rsp,_C_LABEL(startsp)(%rip)
+ /*
+ * No adjustment like in main because entry point is special
+ * and the amd64 csu __start routine takes care of it.
+ *
+ * XXX Why don't we just arrange to align it in the kernel
+ * anyway?
+ */
+ jmp _C_LABEL(__start)
+END(execsp_start)
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+ENTRY(execsp_ctor)
+ /*
+ * `The end of the input argument area shall be aligned on a
+ * [16-byte] boundary. In other words, the value of (%rsp + 8)
+ * is always a multiple of 16 when control is transferred to
+ * the function entry point.'
+ *
+ * To make it convenient for t_signal_and_sp.c, we subtract 8
+ * from %rsp in order to get something congruent to zero modulo
+ * the stack alignemnt.
+ */
+ movq %rsp,_C_LABEL(ctorsp)(%rip)
+ addq $-8,_C_LABEL(ctorsp)(%rip)
+ ret
+END(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .section .ctors,"aw",@progbits
+ .p2align 3
+ .quad _C_LABEL(execsp_ctor)
+
+/*
+ * int main(int argc@rdi, char **argv@rsi, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+ENTRY(main)
+ movq %rsp,_C_LABEL(mainsp)(%rip)
+ addq $-8,_C_LABEL(mainsp)(%rip)
+ xorl %eax,%eax
+ ret
+END(main)
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+ENTRY(execsp_dtor)
+ movq %rsp,_C_LABEL(dtorsp)(%rip)
+ addq $-8,_C_LABEL(dtorsp)(%rip)
+ jmp _C_LABEL(execsp_main)
+END(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .section .dtors,"aw",@progbits
+ .p2align 3
+ .quad _C_LABEL(execsp_dtor)
diff --git a/kernel/arch/x86_64/h_execregs.S b/kernel/arch/x86_64/h_execregs.S
new file mode 100644
index 000000000000..638f73940f06
--- /dev/null
+++ b/kernel/arch/x86_64/h_execregs.S
@@ -0,0 +1,82 @@
+/* $NetBSD: h_execregs.S,v 1.1 2025/02/27 00:55:32 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <sys/syscall.h>
+
+#include <machine/asm.h>
+
+#include "execregs.h"
+
+ENTRY(execregs_start)
+ andq $-0x10,%rsp /* align stack to 16-byte boundary */
+
+ /* store registers to a buffer on stack */
+ subq $(NEXECREGS*8),%rsp /* space for NEXECREGS registers */
+ movq %rdi,0*8(%rsp) /* order matches execregs.h */
+ movq %rsi,1*8(%rsp)
+ movq %rdx,2*8(%rsp)
+ movq %r10,3*8(%rsp)
+ movq %r8,4*8(%rsp)
+ movq %r9,5*8(%rsp)
+ movq %rcx,6*8(%rsp)
+ movq %r11,7*8(%rsp)
+ movq %r12,8*8(%rsp)
+ movq %r13,9*8(%rsp)
+ movq %r14,10*8(%rsp)
+ movq %r15,11*8(%rsp)
+ movq %rbp,12*8(%rsp)
+ movq %rax,13*8(%rsp)
+
+ /* call write(STDOUT_FILENO, regs, sizeof(regs)) */
+ movl $0x1,%edi /* arg0 := STDOUT_FILENO */
+ movq %rsp,%rsi /* arg1 := regs */
+ movl $(NEXECREGS*8),%edx /* arg2 := sizeof(regs) */
+ movl $SYS_write,%eax /* syscall number */
+ syscall
+
+ jb 2f /* bail if write failed */
+ cmpq $(NEXECREGS*8),%rax /* bail if wrote wrong # of bytes */
+ jne 2f
+
+ /* call exit(0) */
+ xorl %edi,%edi /* arg0 := 0 */
+1: movl $SYS_exit,%eax /* syscall number */
+ syscall
+ hlt /* paranoia */
+
+2: /* call exit(127) */
+ movl $127,%edi /* arg0 := 127 */
+ jmp 1b
+END(execregs_start)
+
+/* main stub to simplify linking */
+ENTRY(main)
+ hlt
+END(main)
diff --git a/kernel/arch/x86_64/signalsphandler.S b/kernel/arch/x86_64/signalsphandler.S
new file mode 100644
index 000000000000..b53cb005d0b0
--- /dev/null
+++ b/kernel/arch/x86_64/signalsphandler.S
@@ -0,0 +1,55 @@
+/* $NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:31:01 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:31:01 riastradh Exp $")
+
+/*
+ * signalsphandler(signo@rdi)
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+ENTRY(signalsphandler)
+ /*
+ * `The end of the input argument area shall be aligned on a
+ * [16-byte] boundary. In other words, the value of (%rsp + 8)
+ * is always a multiple of 16 when control is transferred to
+ * the function entry point.'
+ *
+ * To make it convenient for t_signal_and_sp.c, we subtract 8
+ * from %rsp in order to get something congruent to zero modulo
+ * the stack alignemnt.
+ */
+ movq %rsp,_C_LABEL(signalsp)(%rip)
+ addq $-8,_C_LABEL(signalsp)(%rip)
+ ret
+END(signalsphandler)
diff --git a/kernel/arch/x86_64/stack_pointer.h b/kernel/arch/x86_64/stack_pointer.h
new file mode 100644
index 000000000000..6ea297cf97d9
--- /dev/null
+++ b/kernel/arch/x86_64/stack_pointer.h
@@ -0,0 +1,35 @@
+/* $NetBSD: stack_pointer.h,v 1.1 2025/04/20 22:31:01 riastradh Exp $ */
+
+/*
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TESTS_KERNEL_ARCH_X86_64_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_X86_64_STACK_POINTER_H
+
+#define MISALIGN_SP __asm __volatile("addq $-1,%rsp")
+#define FIX_SP __asm __volatile("addq $1,%rsp")
+
+#endif /* TESTS_KERNEL_ARCH_X86_64_STACK_POINTER_H */
diff --git a/kernel/arch/x86_64/threadspfunc.S b/kernel/arch/x86_64/threadspfunc.S
new file mode 100644
index 000000000000..c939f36b408a
--- /dev/null
+++ b/kernel/arch/x86_64/threadspfunc.S
@@ -0,0 +1,54 @@
+/* $NetBSD: threadspfunc.S,v 1.2 2025/04/21 12:06:08 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _LOCORE
+
+#include <machine/asm.h>
+
+RCSID("$NetBSD: threadspfunc.S,v 1.2 2025/04/21 12:06:08 riastradh Exp $")
+
+/*
+ * void *threadspfunc(void *cookie@rdi)
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+ENTRY(threadspfunc)
+ /*
+ * `The end of the input argument area shall be aligned on a
+ * [16-byte] boundary. In other words, the value of (%rsp + 8)
+ * is always a multiple of 16 when control is transferred to
+ * the function entry point.'
+ *
+ * To make it convenient for t_signal_and_sp.c, we subtract 8
+ * from %rsp in order to get something congruent to zero modulo
+ * the stack alignemnt.
+ */
+ movq %rsp,%rax
+ addq $-8,%rax
+ ret
+END(threadspfunc)
diff --git a/kernel/h_cloexec.c b/kernel/h_cloexec.c
new file mode 100644
index 000000000000..55ca4978497f
--- /dev/null
+++ b/kernel/h_cloexec.c
@@ -0,0 +1,48 @@
+/* $NetBSD: h_cloexec.c,v 1.1 2024/11/10 15:57:32 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: h_cloexec.c,v 1.1 2024/11/10 15:57:32 riastradh Exp $");
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int
+main(int argc, char **argv)
+{
+ int fd = atoi(argv[1]);
+ int flags;
+
+ if (fcntl(fd, F_GETFL, &flags) == 0)
+ return 1;
+ if (errno != EBADF)
+ return 2;
+ return 0;
+}
diff --git a/kernel/h_execregs_unimpl.c b/kernel/h_execregs_unimpl.c
new file mode 100644
index 000000000000..606781a2f745
--- /dev/null
+++ b/kernel/h_execregs_unimpl.c
@@ -0,0 +1,37 @@
+/* $NetBSD: h_execregs_unimpl.c,v 1.1 2025/02/27 00:55:31 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: h_execregs_unimpl.c,v 1.1 2025/02/27 00:55:31 riastradh Exp $");
+
+int
+main(void)
+{
+
+ return 127;
+}
diff --git a/kernel/h_execsp.c b/kernel/h_execsp.c
new file mode 100644
index 000000000000..dac1919e4417
--- /dev/null
+++ b/kernel/h_execsp.c
@@ -0,0 +1,68 @@
+/* $NetBSD: h_execsp.c,v 1.2 2025/04/20 22:31:25 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: h_execsp.c,v 1.2 2025/04/20 22:31:25 riastradh Exp $");
+
+#include <err.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "h_execsp.h"
+
+/*
+ * The machine-dependent execsp.S assembly routines will initialize
+ * startsp, ctorsp, mainsp, and dtorsp, and then call execsp_main on
+ * program startup.
+ */
+void *startsp;
+void *ctorsp;
+void *mainsp;
+void *dtorsp;
+
+int execsp_main(void);
+int
+execsp_main(void)
+{
+ struct execsp execsp;
+ ssize_t nwrit;
+
+ memset(&execsp, 0, sizeof(execsp));
+ execsp.startsp = startsp;
+ execsp.ctorsp = ctorsp;
+ execsp.mainsp = mainsp;
+ execsp.dtorsp = dtorsp;
+
+ nwrit = write(STDOUT_FILENO, &execsp, sizeof(execsp));
+ if (nwrit == -1)
+ err(1, "write");
+ if ((size_t)nwrit != sizeof(execsp))
+ errx(1, "wrote %zu != %zu", (size_t)nwrit, sizeof(execsp));
+
+ return 0;
+}
diff --git a/kernel/h_execsp.h b/kernel/h_execsp.h
new file mode 100644
index 000000000000..e94ceaab9c1b
--- /dev/null
+++ b/kernel/h_execsp.h
@@ -0,0 +1,47 @@
+/* $NetBSD: h_execsp.h,v 1.2 2025/04/20 22:31:25 riastradh Exp $ */
+
+/*
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TESTS_KERNEL_H_EXECSP_H
+#define TESTS_KERNEL_H_EXECSP_H
+
+/*
+ * struct execsp
+ *
+ * Structure passed from the h_execsp_* programs to the
+ * t_signal_and_sp test, giving the stack pointer as it was at the
+ * ELF entry point, an ELF constructor, the main function, and an
+ * ELF destructor.
+ */
+struct execsp {
+ void *startsp;
+ void *ctorsp;
+ void *mainsp;
+ void *dtorsp;
+};
+
+#endif /* TESTS_KERNEL_H_EXECSP_H */
diff --git a/kernel/setjmp_tester/Makefile b/kernel/setjmp_tester/Makefile
new file mode 100644
index 000000000000..18492f35fd4a
--- /dev/null
+++ b/kernel/setjmp_tester/Makefile
@@ -0,0 +1,12 @@
+# $NetBSD: Makefile,v 1.1 2025/04/27 16:22:26 riastradh Exp $
+
+KMOD= setjmp_tester
+KMODULEDIR= ${TESTSBASE}/kernel/${KMOD}
+
+SRCS= setjmp_tester.c
+
+ATFFILE= no
+NOMAN= # defined
+
+.include <bsd.test.mk>
+.include <bsd.kmodule.mk>
diff --git a/kernel/setjmp_tester/setjmp_tester.c b/kernel/setjmp_tester/setjmp_tester.c
new file mode 100644
index 000000000000..8b15b840effe
--- /dev/null
+++ b/kernel/setjmp_tester/setjmp_tester.c
@@ -0,0 +1,147 @@
+/* $NetBSD: setjmp_tester.c,v 1.1 2025/04/27 16:22:26 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: setjmp_tester.c,v 1.1 2025/04/27 16:22:26 riastradh Exp $");
+
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+
+MODULE(MODULE_CLASS_MISC, setjmp_tester, NULL);
+
+static struct sysctllog *setjmp_tester_sysctllog;
+static const struct sysctlnode *setjmp_tester_sysctlnode;
+static kmutex_t setjmp_tester_lock;
+static bool setjmp_tester_done;
+static label_t setjmp_tester_label;
+
+__noinline
+static void
+setjmp_tester_subroutine(void)
+{
+
+ printf("%s: call longjmp\n", __func__);
+ setjmp_tester_done = true;
+ longjmp(&setjmp_tester_label);
+ printf("%s: unreachable\n", __func__);
+}
+
+static int
+setjmp_tester_test(void)
+{
+ int result;
+
+ mutex_enter(&setjmp_tester_lock);
+
+ setjmp_tester_done = false;
+ result = setjmp(&setjmp_tester_label);
+ if (!setjmp_tester_done) {
+ printf("%s: setjmp returned %d at first\n", __func__, result);
+ if (result != 0) {
+ result = -1;
+ goto out;
+ }
+ setjmp_tester_subroutine();
+ /*NOTREACHED*/
+ printf("%s: setjmp_tester_subroutine returned\n", __func__);
+ result = -1;
+ } else {
+ printf("%s: setjmp returned %d at second\n", __func__, result);
+ if (result == 0) {
+ result = -2;
+ goto out;
+ }
+ }
+
+out: mutex_exit(&setjmp_tester_lock);
+ return result;
+}
+
+static int
+setjmp_tester_test_sysctl(SYSCTLFN_ARGS)
+{
+ struct sysctlnode node = *rnode;
+ int v = 0;
+ int error;
+
+ if (newp == NULL) {
+ error = ENOENT;
+ goto out;
+ }
+ error = sysctl_copyin(curlwp, newp, &v, sizeof(v));
+ if (error)
+ goto out;
+ switch (v) {
+ case 1:
+ v = setjmp_tester_test();
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ node.sysctl_data = &v;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+
+out: return error;
+}
+
+static int
+setjmp_tester_modcmd(modcmd_t cmd, void *arg)
+{
+ int error = 0;
+
+ switch (cmd) {
+ case MODULE_CMD_INIT:
+ mutex_init(&setjmp_tester_lock, MUTEX_DEFAULT, IPL_NONE);
+ error = sysctl_createv(&setjmp_tester_sysctllog, 0,
+ NULL, &setjmp_tester_sysctlnode,
+ CTLFLAG_PERMANENT, CTLTYPE_NODE, "setjmp_tester",
+ SYSCTL_DESCR("setjmp/longjmp testing interface"),
+ NULL, 0, NULL, 0,
+ CTL_KERN, CTL_CREATE, CTL_EOL);
+ if (error)
+ goto fini;
+ error = sysctl_createv(&setjmp_tester_sysctllog, 0,
+ &setjmp_tester_sysctlnode, NULL,
+ CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "test",
+ SYSCTL_DESCR("setjmp/longjmp test trigger"),
+ &setjmp_tester_test_sysctl, 0, NULL, 0,
+ CTL_CREATE, CTL_EOL);
+ if (error)
+ goto fini;
+ return error;
+ case MODULE_CMD_FINI:
+ fini:
+ sysctl_teardown(&setjmp_tester_sysctllog);
+ mutex_destroy(&setjmp_tester_lock);
+ return error;
+ default:
+ return ENOTTY;
+ }
+}
diff --git a/kernel/t_cloexec.c b/kernel/t_cloexec.c
new file mode 100644
index 000000000000..c0150af8ca38
--- /dev/null
+++ b/kernel/t_cloexec.c
@@ -0,0 +1,457 @@
+/* $NetBSD: t_cloexec.c,v 1.1 2024/11/10 15:57:32 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_cloexec.c,v 1.1 2024/11/10 15:57:32 riastradh Exp $");
+
+#include <sys/types.h>
+
+#include <sys/bitops.h>
+#include <sys/event.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <spawn.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "h_macros.h"
+
+/*
+ * Test close-on-exec as set in various ways
+ */
+
+static int
+open_via_accept4(void)
+{
+ static const union {
+ struct sockaddr sa;
+ struct sockaddr_un sun;
+ } name = { .sun = {
+ .sun_family = AF_LOCAL,
+ .sun_path = "socket",
+ } };
+ int slisten, saccept, c;
+
+ /*
+ * Create a listening server socket and bind it to the path.
+ */
+ RL(slisten = socket(PF_LOCAL, SOCK_STREAM, 0));
+ RL(bind(slisten, &name.sa, sizeof(name)));
+ RL(listen(slisten, SOMAXCONN));
+
+ /*
+ * Create an active client socket and connect it to the path --
+ * nonblocking, so we don't deadlock here. If connect doesn't
+ * succeed immediately, it had better fail immediately with
+ * EINPROGRESS.
+ */
+ RL(c = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0));
+ if (connect(c, &name.sa, sizeof(name)) == -1) {
+ ATF_CHECK_EQ_MSG(errno, EINPROGRESS, "connect failed %d: %s",
+ errno, strerror(errno));
+ }
+
+ /*
+ * Accept a socket on the server side with SOCK_CLOEXEC.
+ */
+ RL(saccept = accept4(slisten, /*addr*/NULL, /*addrlen*/NULL,
+ SOCK_CLOEXEC));
+ return saccept;
+}
+
+static int
+open_via_clonedev(void)
+{
+ int fd;
+
+ RL(fd = open("/dev/drvctl", O_RDONLY|O_CLOEXEC));
+
+ return fd;
+}
+
+static int
+open_via_dup3(void)
+{
+ int fd3;
+
+ RL(fd3 = dup3(STDIN_FILENO, 3, O_CLOEXEC));
+ ATF_REQUIRE_EQ_MSG(fd3, 3, "dup3(STDIN_FILENO, 3, ...)"
+ " failed to return 3: %d", fd3);
+
+ return fd3;
+}
+
+static int
+open_via_fcntldupfd(void)
+{
+ int fd;
+
+ RL(fd = fcntl(STDIN_FILENO, F_DUPFD_CLOEXEC, 0));
+
+ return fd;
+}
+
+static int
+open_via_kqueue(void)
+{
+ int fd;
+
+ RL(fd = kqueue1(O_CLOEXEC));
+
+ return fd;
+}
+
+static int
+open_via_opencloexec(void)
+{
+ int fd;
+
+ RL(fd = open("file", O_RDWR|O_CREAT|O_CLOEXEC, 0644));
+
+ return fd;
+}
+
+static int
+open_via_openfcntlcloexec(void)
+{
+ int fd;
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0644));
+ RL(fcntl(fd, F_SETFD, FD_CLOEXEC));
+
+ return fd;
+}
+
+static int
+open_via_openioctlfioclex(void)
+{
+ int fd;
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0644));
+ RL(ioctl(fd, FIOCLEX));
+
+ return fd;
+}
+
+static int
+open_via_pipe2rd(void)
+{
+ int fd[2];
+
+ RL(pipe2(fd, O_CLOEXEC));
+
+ return fd[0];
+}
+
+static int
+open_via_pipe2wr(void)
+{
+ int fd[2];
+
+ RL(pipe2(fd, O_CLOEXEC));
+
+ return fd[1];
+}
+
+static int
+open_via_paccept(void)
+{
+ static const union {
+ struct sockaddr sa;
+ struct sockaddr_un sun;
+ } name = { .sun = {
+ .sun_family = AF_LOCAL,
+ .sun_path = "socket",
+ } };
+ int slisten, saccept, c;
+
+ /*
+ * Create a listening server socket and bind it to the path.
+ */
+ RL(slisten = socket(PF_LOCAL, SOCK_STREAM, 0));
+ RL(bind(slisten, &name.sa, sizeof(name)));
+ RL(listen(slisten, SOMAXCONN));
+
+ /*
+ * Create an active client socket and connect it to the path --
+ * nonblocking, so we don't deadlock here. If connect doesn't
+ * succeed immediately, it had better fail immediately with
+ * EINPROGRESS.
+ */
+ RL(c = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0));
+ if (connect(c, &name.sa, sizeof(name)) == -1) {
+ ATF_CHECK_EQ_MSG(errno, EINPROGRESS, "connect failed %d: %s",
+ errno, strerror(errno));
+ }
+
+ /*
+ * Accept a socket on the server side with SOCK_CLOEXEC.
+ */
+ RL(saccept = paccept(slisten, /*addr*/NULL, /*addrlen*/NULL,
+ /*sigmask*/NULL, SOCK_CLOEXEC));
+ return saccept;
+}
+
+static int
+open_via_socket(void)
+{
+ int fd;
+
+ RL(fd = socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC, 0));
+
+ return fd;
+}
+
+static int
+open_via_socketpair0(void)
+{
+ int fd[2];
+
+ RL(socketpair(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC, 0, fd));
+
+ return fd[0];
+}
+
+static int
+open_via_socketpair1(void)
+{
+ int fd[2];
+
+ RL(socketpair(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC, 0, fd));
+
+ return fd[1];
+}
+
+/*
+ * XXX Close-on-exec paths still missing:
+ * XXX
+ * XXX compat_linux inotify
+ * XXX compat_linux close_range
+ * XXX drm i915_perf_open_ioctl
+ * XXX drm dma_buf
+ * XXX eventfd(2)
+ * XXX memfd(2)
+ * XXX timerfd(2)
+ * XXX recvmsg/recvmmsg with MSG_CMSG_CLOEXEC
+ */
+
+static void
+check_cloexec(const struct atf_tc *tc, int fd,
+ pid_t (*execfn)(char *, char *const[]))
+{
+ char h_cloexec[PATH_MAX];
+ char fdstr[(ilog2(INT_MAX) + 1)/(ilog2(10) - 1) + 1];
+ char *const argv[] = {__UNCONST("h_cloexec"), fdstr, NULL};
+ pid_t child, waitedpid;
+ int status;
+
+ /*
+ * Format the h_cloexec helper executable path, which lives in
+ * the test's directory (typically /usr/tests/kernel), and the
+ * argument of a file descriptor in decimal.
+ */
+ snprintf(h_cloexec, sizeof(h_cloexec), "%s/h_cloexec",
+ atf_tc_get_config_var(tc, "srcdir"));
+ snprintf(fdstr, sizeof(fdstr), "%d", fd);
+
+ /*
+ * Execute h_cloexec as a subprocess.
+ */
+ child = (*execfn)(h_cloexec, argv);
+
+ /*
+ * Wait for the child to complete.
+ */
+ RL(waitedpid = waitpid(child, &status, 0));
+ ATF_CHECK_EQ_MSG(child, waitedpid, "waited for %jd, got %jd",
+ (intmax_t)child, (intmax_t)waitedpid);
+
+ /*
+ * Verify the child exited normally.
+ */
+ if (WIFSIGNALED(status)) {
+ atf_tc_fail("subprocess terminated on signal %d",
+ WTERMSIG(status));
+ return;
+ } else if (!WIFEXITED(status)) {
+ atf_tc_fail("subprocess failed to exit normally: status=0x%x",
+ status);
+ return;
+ }
+
+ /*
+ * h_cloexec is supposed to exit status 0 if an operation on
+ * the fd failed with EBADFD, 1 if it unexpectedly succeeded,
+ * 127 if exec returned, or something else if anything else
+ * happened.
+ */
+ switch (WEXITSTATUS(status)) {
+ case 0: /* success -- closed on exec */
+ return;
+ case 1: /* fail -- not closed on exec */
+ atf_tc_fail("fd was not closed on exec");
+ return;
+ case 127: /* exec failed */
+ atf_tc_fail("failed to exec h_cloexec");
+ return;
+ default: /* something else went wong */
+ atf_tc_fail("h_cloexec failed unexpectedly: %d",
+ WEXITSTATUS(status));
+ return;
+ }
+}
+
+static pid_t
+exec_via_forkexecve(char *prog, char *const argv[])
+{
+ pid_t pid;
+
+ RL(pid = fork());
+ if (pid == 0) { /* child */
+ if (execve(prog, argv, /*envp*/NULL) == -1)
+ _exit(127);
+ abort();
+ }
+
+ /* parent */
+ return pid;
+}
+
+static pid_t
+exec_via_vforkexecve(char *prog, char *const argv[])
+{
+ pid_t pid;
+
+ RL(pid = vfork());
+ if (pid == 0) { /* child */
+ if (execve(prog, argv, /*envp*/NULL) == -1)
+ _exit(127);
+ abort();
+ }
+
+ /* parent */
+ return pid;
+}
+
+static pid_t
+exec_via_posixspawn(char *prog, char *const argv[])
+{
+ pid_t pid;
+
+ RZ(posix_spawn(&pid, prog, /*file_actions*/NULL, /*attrp*/NULL, argv,
+ /*envp*/NULL));
+
+ return pid;
+}
+
+/*
+ * Full cartesian product is not really important here -- the paths for
+ * open and the paths for exec are independent. So we try
+ * pipe2(O_CLOEXEC) with each exec path, and we try each open path with
+ * posix_spawn.
+ */
+
+#define CLOEXEC_TEST(test, openvia, execvia, descr) \
+ATF_TC(test); \
+ATF_TC_HEAD(test, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", descr); \
+} \
+ATF_TC_BODY(test, tc) \
+{ \
+ check_cloexec(tc, openvia(), &execvia); \
+}
+
+CLOEXEC_TEST(pipe2rd_forkexecve, open_via_pipe2rd, exec_via_forkexecve,
+ "pipe2(O_CLOEXEC) reader is closed in child on fork/exec")
+CLOEXEC_TEST(pipe2rd_vforkexecve, open_via_pipe2rd, exec_via_vforkexecve,
+ "pipe2(O_CLOEXEC) reader is closed in child on vfork/exec")
+CLOEXEC_TEST(pipe2rd_posixspawn, open_via_pipe2rd, exec_via_posixspawn,
+ "pipe2(O_CLOEXEC) reader is closed in child on posix_spawn")
+
+CLOEXEC_TEST(accept4_posixspawn, open_via_accept4, exec_via_posixspawn,
+ "accept4(SOCK_CLOEXEC) is closed in child on posix_spawn");
+CLOEXEC_TEST(clonedev_posixspawn, open_via_clonedev, exec_via_posixspawn,
+ "open(\"/dev/drvctl\") is closed in child on posix_spawn");
+CLOEXEC_TEST(dup3_posixspawn, open_via_dup3, exec_via_posixspawn,
+ "dup3(..., O_CLOEXEC) is closed in child on posix_spawn");
+CLOEXEC_TEST(fcntldupfd_posixspawn, open_via_fcntldupfd, exec_via_posixspawn,
+ "fcntl(STDIN_FILENO, F_DUPFD_CLOEXEC) is closed in child on posix_spawn");
+CLOEXEC_TEST(kqueue_posixspawn, open_via_kqueue, exec_via_posixspawn,
+ "kqueue1(O_CLOEXEC) is closed in child on posix_spawn");
+CLOEXEC_TEST(opencloexec_posixspawn, open_via_opencloexec, exec_via_posixspawn,
+ "open(O_CLOEXEC) is closed in child on posix_spawn");
+CLOEXEC_TEST(openfcntlcloexec_posixspawn, open_via_openfcntlcloexec,
+ exec_via_posixspawn,
+ "fcntl(open(...), F_SETFD, O_CLOEXEC) is closed in child on posix_spawn");
+CLOEXEC_TEST(openioctlfioclex_posixspawn, open_via_openioctlfioclex,
+ exec_via_posixspawn,
+ "ioctl(open(...), FIOCLEX) is closed in child on posix_spawn");
+#if 0 /* already done above */
+CLOEXEC_TEST(pipe2rd_posixspawn, open_via_pipe2rd, exec_via_posixspawn,
+ "pipe2(O_CLOEXEC) reader is closed in child on posix_spawn")
+#endif
+CLOEXEC_TEST(pipe2wr_posixspawn, open_via_pipe2wr, exec_via_posixspawn,
+ "pipe2(O_CLOEXEC) writer is closed in child on posix_spawn")
+CLOEXEC_TEST(paccept_posixspawn, open_via_paccept, exec_via_posixspawn,
+ "paccept(..., SOCK_CLOEXEC) is closed in child on posix_spawn")
+CLOEXEC_TEST(socket_posixspawn, open_via_socket, exec_via_posixspawn,
+ "socket(SOCK_CLOEXEC) is closed in child on posix_spawn")
+CLOEXEC_TEST(socketpair0_posixspawn, open_via_socketpair0, exec_via_posixspawn,
+ "socketpair(SOCK_CLOEXEC) side 0 is closed in child on posix_spawn")
+CLOEXEC_TEST(socketpair1_posixspawn, open_via_socketpair1, exec_via_posixspawn,
+ "socketpair(SOCK_CLOEXEC) side 1 is closed in child on posix_spawn")
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, accept4_posixspawn);
+ ATF_TP_ADD_TC(tp, clonedev_posixspawn);
+ ATF_TP_ADD_TC(tp, dup3_posixspawn);
+ ATF_TP_ADD_TC(tp, fcntldupfd_posixspawn);
+ ATF_TP_ADD_TC(tp, kqueue_posixspawn);
+ ATF_TP_ADD_TC(tp, opencloexec_posixspawn);
+ ATF_TP_ADD_TC(tp, openfcntlcloexec_posixspawn);
+ ATF_TP_ADD_TC(tp, openioctlfioclex_posixspawn);
+ ATF_TP_ADD_TC(tp, paccept_posixspawn);
+ ATF_TP_ADD_TC(tp, pipe2rd_forkexecve);
+ ATF_TP_ADD_TC(tp, pipe2rd_posixspawn);
+ ATF_TP_ADD_TC(tp, pipe2rd_vforkexecve);
+ ATF_TP_ADD_TC(tp, pipe2wr_posixspawn);
+ ATF_TP_ADD_TC(tp, socket_posixspawn);
+ ATF_TP_ADD_TC(tp, socketpair0_posixspawn);
+ ATF_TP_ADD_TC(tp, socketpair1_posixspawn);
+
+ return atf_no_error();
+}
diff --git a/kernel/t_clofork.c b/kernel/t_clofork.c
new file mode 100644
index 000000000000..9c1ba81c733a
--- /dev/null
+++ b/kernel/t_clofork.c
@@ -0,0 +1,445 @@
+/* $NetBSD: t_clofork.c,v 1.1 2025/07/17 19:50:40 kre Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Adapted from t_cloexec.c */
+
+#include <sys/cdefs.h>
+
+#include <sys/types.h>
+
+#include <sys/bitops.h>
+#include <sys/event.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <spawn.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "h_macros.h"
+
+#if defined(O_CLOFORK) && O_CLOFORK != 0
+/*
+ * Test close-on-fork as set in various ways
+ */
+
+static int
+open_via_accept4(void)
+{
+ static const union {
+ struct sockaddr sa;
+ struct sockaddr_un sun;
+ } name = { .sun = {
+ .sun_family = AF_LOCAL,
+ .sun_path = "socket",
+ } };
+ int slisten, saccept, c;
+
+ /*
+ * Create a listening server socket and bind it to the path.
+ */
+ RL(slisten = socket(PF_LOCAL, SOCK_STREAM, 0));
+ RL(bind(slisten, &name.sa, sizeof(name)));
+ RL(listen(slisten, SOMAXCONN));
+
+ /*
+ * Create an active client socket and connect it to the path --
+ * nonblocking, so we don't deadlock here. If connect doesn't
+ * succeed immediately, it had better fail immediately with
+ * EINPROGRESS.
+ */
+ RL(c = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0));
+ if (connect(c, &name.sa, sizeof(name)) == -1) {
+ ATF_CHECK_EQ_MSG(errno, EINPROGRESS, "connect failed %d: %s",
+ errno, strerror(errno));
+ }
+
+ /*
+ * Accept a socket on the server side with SOCK_CLOFORK.
+ */
+ RL(saccept = accept4(slisten, /*addr*/NULL, /*addrlen*/NULL,
+ SOCK_CLOFORK));
+ return saccept;
+}
+
+static int
+open_via_clonedev(void)
+{
+ int fd;
+
+ RL(fd = open("/dev/drvctl", O_RDONLY|O_CLOFORK));
+
+ return fd;
+}
+
+static int
+open_via_dup3(void)
+{
+ int fd3;
+
+ RL(fd3 = dup3(STDIN_FILENO, 3, O_CLOFORK));
+ ATF_REQUIRE_EQ_MSG(fd3, 3, "dup3(STDIN_FILENO, 3, ...)"
+ " failed to return 3: %d", fd3);
+
+ return fd3;
+}
+
+static int
+open_via_fcntldupfd(void)
+{
+ int fd;
+
+ RL(fd = fcntl(STDIN_FILENO, F_DUPFD_CLOFORK, 0));
+
+ return fd;
+}
+
+static int
+open_via_kqueue(void)
+{
+ int fd;
+
+ RL(fd = kqueue1(O_CLOFORK));
+
+ return fd;
+}
+
+static int
+open_via_openclofork(void)
+{
+ int fd;
+
+ RL(fd = open("file", O_RDWR|O_CREAT|O_CLOFORK, 0644));
+
+ return fd;
+}
+
+static int
+open_via_openfcntlclofork(void)
+{
+ int fd;
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0644));
+ RL(fcntl(fd, F_SETFD, FD_CLOFORK));
+
+ return fd;
+}
+
+static int
+open_via_pipe2rd(void)
+{
+ int fd[2];
+
+ RL(pipe2(fd, O_CLOFORK));
+
+ return fd[0];
+}
+
+static int
+open_via_pipe2wr(void)
+{
+ int fd[2];
+
+ RL(pipe2(fd, O_CLOFORK));
+
+ return fd[1];
+}
+
+static int
+open_via_paccept(void)
+{
+ static const union {
+ struct sockaddr sa;
+ struct sockaddr_un sun;
+ } name = { .sun = {
+ .sun_family = AF_LOCAL,
+ .sun_path = "socket",
+ } };
+ int slisten, saccept, c;
+
+ /*
+ * Create a listening server socket and bind it to the path.
+ */
+ RL(slisten = socket(PF_LOCAL, SOCK_STREAM, 0));
+ RL(bind(slisten, &name.sa, sizeof(name)));
+ RL(listen(slisten, SOMAXCONN));
+
+ /*
+ * Create an active client socket and connect it to the path --
+ * nonblocking, so we don't deadlock here. If connect doesn't
+ * succeed immediately, it had better fail immediately with
+ * EINPROGRESS.
+ */
+ RL(c = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0));
+ if (connect(c, &name.sa, sizeof(name)) == -1) {
+ ATF_CHECK_EQ_MSG(errno, EINPROGRESS, "connect failed %d: %s",
+ errno, strerror(errno));
+ }
+
+ /*
+ * Accept a socket on the server side with SOCK_CLOFORK.
+ */
+ RL(saccept = paccept(slisten, /*addr*/NULL, /*addrlen*/NULL,
+ /*sigmask*/NULL, SOCK_CLOFORK));
+ return saccept;
+}
+
+static int
+open_via_socket(void)
+{
+ int fd;
+
+ RL(fd = socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOFORK, 0));
+
+ return fd;
+}
+
+static int
+open_via_socketpair0(void)
+{
+ int fd[2];
+
+ RL(socketpair(PF_LOCAL, SOCK_STREAM|SOCK_CLOFORK, 0, fd));
+
+ return fd[0];
+}
+
+static int
+open_via_socketpair1(void)
+{
+ int fd[2];
+
+ RL(socketpair(PF_LOCAL, SOCK_STREAM|SOCK_CLOFORK, 0, fd));
+
+ return fd[1];
+}
+
+static void
+check_clofork(const struct atf_tc *tc, int fd,
+ pid_t (*execfn)(char *, char *const[]))
+{
+ char h_clofork[PATH_MAX];
+ char fdstr[(ilog2(INT_MAX) + 1)/(ilog2(10) - 1) + 1];
+ char *const argv[] = {__UNCONST("h_cloexec"), fdstr, NULL};
+ pid_t child, waitedpid;
+ int status;
+
+ /*
+ * Format the h_clofork helper executable path, which lives in
+ * the test's directory (typically /usr/tests/kernel), and the
+ * argument of a file descriptor in decimal.
+ */
+ snprintf(h_clofork, sizeof(h_clofork), "%s/h_cloexec",
+ atf_tc_get_config_var(tc, "srcdir"));
+ snprintf(fdstr, sizeof(fdstr), "%d", fd);
+
+ /*
+ * Execute h_clofork as a subprocess.
+ */
+ child = (*execfn)(h_clofork, argv);
+
+ /*
+ * Wait for the child to complete.
+ */
+ RL(waitedpid = waitpid(child, &status, 0));
+ ATF_CHECK_EQ_MSG(child, waitedpid, "waited for %jd, got %jd",
+ (intmax_t)child, (intmax_t)waitedpid);
+
+ /*
+ * Verify the child exited normally.
+ */
+ if (WIFSIGNALED(status)) {
+ atf_tc_fail("subprocess terminated on signal %d",
+ WTERMSIG(status));
+ return;
+ } else if (!WIFEXITED(status)) {
+ atf_tc_fail("subprocess failed to exit normally: status=0x%x",
+ status);
+ return;
+ }
+
+ /*
+ * h_clofork is supposed to exit status 0 if an operation on
+ * the fd failed with EBADFD, 1 if it unexpectedly succeeded,
+ * 127 if exec returned, or something else if anything else
+ * happened.
+ */
+ switch (WEXITSTATUS(status)) {
+ case 0: /* success -- closed on exec */
+ return;
+ case 1: /* fail -- not closed on exec */
+ atf_tc_fail("fd was not closed on exec");
+ return;
+ case 127: /* exec failed */
+ atf_tc_fail("failed to exec h_cloexec");
+ return;
+ default: /* something else went wong */
+ atf_tc_fail("h_cloexec failed unexpectedly: %d",
+ WEXITSTATUS(status));
+ return;
+ }
+}
+
+static pid_t
+exec_via_forkexecve(char *prog, char *const argv[])
+{
+ pid_t pid;
+
+ RL(pid = fork());
+ if (pid == 0) { /* child */
+ if (execve(prog, argv, /*envp*/NULL) == -1)
+ _exit(127);
+ abort();
+ }
+
+ /* parent */
+ return pid;
+}
+
+static pid_t
+exec_via_vforkexecve(char *prog, char *const argv[])
+{
+ pid_t pid;
+
+ RL(pid = vfork());
+ if (pid == 0) { /* child */
+ if (execve(prog, argv, /*envp*/NULL) == -1)
+ _exit(127);
+ abort();
+ }
+
+ /* parent */
+ return pid;
+}
+
+static pid_t
+exec_via_posixspawn(char *prog, char *const argv[])
+{
+ pid_t pid;
+
+ RZ(posix_spawn(&pid, prog, /*file_actions*/NULL, /*attrp*/NULL, argv,
+ /*envp*/NULL));
+
+ return pid;
+}
+
+/*
+ * Full cartesian product is not really important here -- the paths for
+ * open and the paths for exec are independent. So we try
+ * pipe2(O_CLOFORK) with each exec path, and we try each open path with
+ * posix_spawn.
+ */
+
+#define CLOFORK_TEST(test, openvia, execvia, descr) \
+ATF_TC(test); \
+ATF_TC_HEAD(test, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", descr); \
+} \
+ATF_TC_BODY(test, tc) \
+{ \
+ check_clofork(tc, openvia(), &execvia); \
+}
+
+CLOFORK_TEST(pipe2rd_forkexecve, open_via_pipe2rd, exec_via_forkexecve,
+ "pipe2(O_CLOFORK) reader is closed in child on fork/exec")
+CLOFORK_TEST(pipe2rd_vforkexecve, open_via_pipe2rd, exec_via_vforkexecve,
+ "pipe2(O_CLOFORK) reader is closed in child on vfork/exec")
+CLOFORK_TEST(pipe2rd_posixspawn, open_via_pipe2rd, exec_via_posixspawn,
+ "pipe2(O_CLOFORK) reader is closed in child on posix_spawn")
+
+CLOFORK_TEST(accept4_posixspawn, open_via_accept4, exec_via_posixspawn,
+ "accept4(SOCK_CLOFORK) is closed in child on posix_spawn");
+CLOFORK_TEST(clonedev_posixspawn, open_via_clonedev, exec_via_posixspawn,
+ "open(\"/dev/drvctl\") is closed in child on posix_spawn");
+CLOFORK_TEST(dup3_posixspawn, open_via_dup3, exec_via_posixspawn,
+ "dup3(..., O_CLOFORK) is closed in child on posix_spawn");
+CLOFORK_TEST(fcntldupfd_posixspawn, open_via_fcntldupfd, exec_via_posixspawn,
+ "fcntl(STDIN_FILENO, F_DUPFD_CLOFORK) is closed in child on posix_spawn");
+CLOFORK_TEST(kqueue_posixspawn, open_via_kqueue, exec_via_posixspawn,
+ "kqueue1(O_CLOFORK) is closed in child on posix_spawn");
+CLOFORK_TEST(openclofork_posixspawn, open_via_openclofork, exec_via_posixspawn,
+ "open(O_CLOFORK) is closed in child on posix_spawn");
+CLOFORK_TEST(openfcntlclofork_posixspawn, open_via_openfcntlclofork,
+ exec_via_posixspawn,
+ "fcntl(open(...), F_SETFD, O_CLOFORK) is closed in child on posix_spawn");
+CLOFORK_TEST(pipe2wr_posixspawn, open_via_pipe2wr, exec_via_posixspawn,
+ "pipe2(O_CLOFORK) writer is closed in child on posix_spawn")
+CLOFORK_TEST(paccept_posixspawn, open_via_paccept, exec_via_posixspawn,
+ "paccept(..., SOCK_CLOFORK) is closed in child on posix_spawn")
+CLOFORK_TEST(socket_posixspawn, open_via_socket, exec_via_posixspawn,
+ "socket(SOCK_CLOFORK) is closed in child on posix_spawn")
+CLOFORK_TEST(socketpair0_posixspawn, open_via_socketpair0, exec_via_posixspawn,
+ "socketpair(SOCK_CLOFORK) side 0 is closed in child on posix_spawn")
+CLOFORK_TEST(socketpair1_posixspawn, open_via_socketpair1, exec_via_posixspawn,
+ "socketpair(SOCK_CLOFORK) side 1 is closed in child on posix_spawn")
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, accept4_posixspawn);
+ ATF_TP_ADD_TC(tp, clonedev_posixspawn);
+ ATF_TP_ADD_TC(tp, dup3_posixspawn);
+ ATF_TP_ADD_TC(tp, fcntldupfd_posixspawn);
+ ATF_TP_ADD_TC(tp, kqueue_posixspawn);
+ ATF_TP_ADD_TC(tp, openclofork_posixspawn);
+ ATF_TP_ADD_TC(tp, openfcntlclofork_posixspawn);
+ ATF_TP_ADD_TC(tp, paccept_posixspawn);
+ ATF_TP_ADD_TC(tp, pipe2rd_forkexecve);
+ ATF_TP_ADD_TC(tp, pipe2rd_posixspawn);
+ ATF_TP_ADD_TC(tp, pipe2rd_vforkexecve);
+ ATF_TP_ADD_TC(tp, pipe2wr_posixspawn);
+ ATF_TP_ADD_TC(tp, socket_posixspawn);
+ ATF_TP_ADD_TC(tp, socketpair0_posixspawn);
+ ATF_TP_ADD_TC(tp, socketpair1_posixspawn);
+
+ return atf_no_error();
+}
+
+#else /* No O_CLOFORK */
+
+ATF_TC(not_implemented);
+ATF_TC_HEAD(not_implemented, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Unimplemented O_CLOFORK");
+}
+ATF_TC_BODY(not_implemented, tc)
+{
+ atf_tc_skip("close-on-fork not yet available");
+}
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, not_implemented);
+
+ return atf_no_error();
+}
+#endif
diff --git a/kernel/t_execregs.c b/kernel/t_execregs.c
new file mode 100644
index 000000000000..076a8de9547c
--- /dev/null
+++ b/kernel/t_execregs.c
@@ -0,0 +1,197 @@
+/* $NetBSD: t_execregs.c,v 1.7 2025/04/27 14:30:03 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_execregs.c,v 1.7 2025/04/27 14:30:03 riastradh Exp $");
+
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <err.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef HAVE_EXECREGS_TEST
+
+#include "execregs.h"
+#include "isqemu.h"
+#include "h_macros.h"
+
+static void
+readregs(int rfd, register_t regs[static NEXECREGS])
+{
+ uint8_t *p;
+ size_t n;
+ ssize_t nread;
+
+ p = (void *)regs;
+ n = NEXECREGS*sizeof(regs[0]);
+ while (n) {
+ RL(nread = read(rfd, p, n));
+ ATF_CHECK_MSG((size_t)nread <= n,
+ "overlong read: %zu > %zu", (size_t)nread, n);
+ if (nread == 0)
+ break;
+ p += (size_t)nread;
+ n -= (size_t)nread;
+ }
+ ATF_CHECK_EQ_MSG(n, 0,
+ "truncated read, missing %zu of %zu bytes",
+ n, NEXECREGS*sizeof(regs[0]));
+}
+
+static void
+checkregs(const register_t regs[static NEXECREGS])
+{
+ unsigned i;
+
+#ifdef __hppa__
+ if (isQEMU()) {
+ atf_tc_expect_fail("PR port-hppa/59114: hppa:"
+ " eager fpu switching for qemu and/or spectre mitigation");
+ }
+#endif
+
+ for (i = 0; i < NEXECREGS; i++) {
+ if (regs[i] != 0) {
+ for (i = 0; i < NEXECREGS; i++) {
+ fprintf(stderr, "[%s] %"PRIxREGISTER"\n",
+ regname[i], regs[i]);
+ }
+ fprintf(stderr, "\n");
+ atf_tc_fail("registers not zeroed");
+ }
+ }
+}
+
+static void
+testregs(int child, const int pipefd[static 2],
+ register_t regs[static NEXECREGS])
+{
+ int status;
+
+ RL(close(pipefd[1]));
+
+ readregs(pipefd[0], regs);
+
+ RL(waitpid(child, &status, 0));
+ if (WIFSIGNALED(status)) {
+ atf_tc_fail_nonfatal("child terminated on signal %d (%s)",
+ WTERMSIG(status), strsignal(WTERMSIG(status)));
+ } else if (!WIFEXITED(status)) {
+ atf_tc_fail_nonfatal("child terminated mysteriously,"
+ " status=0x%x",
+ status);
+ } else {
+ ATF_CHECK_MSG(WEXITSTATUS(status) == 0,
+ "child exited with code %d", WEXITSTATUS(status));
+ }
+
+ checkregs(regs);
+}
+
+#endif
+
+ATF_TC(execregszero);
+ATF_TC_HEAD(execregszero, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test execve(2) zeroes registers");
+}
+ATF_TC_BODY(execregszero, tc)
+{
+#ifdef HAVE_EXECREGS_TEST
+ char h_execregs[PATH_MAX];
+ int pipefd[2];
+ register_t regs[NEXECREGS];
+ pid_t child;
+
+ RL(snprintf(h_execregs, sizeof(h_execregs), "%s/h_execregs",
+ atf_tc_get_config_var(tc, "srcdir")));
+
+ RL(pipe(pipefd));
+ memset(regs, 0x5a, sizeof(regs));
+
+ RL(child = fork());
+ if (child == 0) {
+ if (dup2(pipefd[1], STDOUT_FILENO) == -1)
+ err(1, "dup2");
+ if (closefrom(STDERR_FILENO + 1) == -1)
+ err(1, "closefrom");
+ if (execregschild(h_execregs) == -1)
+ err(1, "execve");
+ _exit(2);
+ }
+
+ testregs(child, pipefd, regs);
+#else
+ atf_tc_skip("missing test for PR kern/59084:"
+ " exec/spawn leaks register content");
+#endif
+}
+
+ATF_TC(spawnregszero);
+ATF_TC_HEAD(spawnregszero, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test posix_spawn(2) zeroes registers");
+}
+ATF_TC_BODY(spawnregszero, tc)
+{
+#ifdef HAVE_EXECREGS_TEST
+ char h_execregs[PATH_MAX];
+ int pipefd[2];
+ register_t regs[NEXECREGS];
+ pid_t child;
+
+ RL(snprintf(h_execregs, sizeof(h_execregs), "%s/h_execregs",
+ atf_tc_get_config_var(tc, "srcdir")));
+
+ RL(pipe(pipefd));
+ memset(regs, 0x5a, sizeof(regs));
+
+ RL(child = spawnregschild(h_execregs, pipefd[1]));
+
+ testregs(child, pipefd, regs);
+#else
+ atf_tc_skip("missing test for PR kern/59084:"
+ " exec/spawn leaks register content");
+#endif
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, execregszero);
+ ATF_TP_ADD_TC(tp, spawnregszero);
+
+ return atf_no_error();
+}
diff --git a/kernel/t_fdrestart.c b/kernel/t_fdrestart.c
new file mode 100644
index 000000000000..5449fcdaf18d
--- /dev/null
+++ b/kernel/t_fdrestart.c
@@ -0,0 +1,274 @@
+/* $NetBSD: t_fdrestart.c,v 1.4 2023/11/18 19:46:55 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2023 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _KMEMUSER /* ERESTART */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_fdrestart.c,v 1.4 2023/11/18 19:46:55 riastradh Exp $");
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_macros.h"
+
+struct fdrestart {
+ void (*op)(struct fdrestart *);
+ int fd;
+ pthread_barrier_t barrier;
+};
+
+static void
+waitforbarrier(struct fdrestart *F, const char *caller)
+{
+ int error;
+
+ error = pthread_barrier_wait(&F->barrier);
+ switch (error) {
+ case 0:
+ case PTHREAD_BARRIER_SERIAL_THREAD:
+ break;
+ default:
+ atf_tc_fail("%s: pthread_barrier_wait: %d, %s", caller, error,
+ strerror(error));
+ }
+}
+
+static void
+doread(struct fdrestart *F)
+{
+ char c;
+ ssize_t nread;
+ int error;
+
+ /*
+ * Wait for the other thread to be ready.
+ */
+ waitforbarrier(F, "reader");
+
+ /*
+ * Start a read. This should block, and then, when the other
+ * thread closes the fd, should be woken to fail with ERESTART.
+ */
+ nread = rump_sys_read(F->fd, &c, sizeof(c));
+ ATF_REQUIRE_EQ_MSG(nread, -1, "nread=%zd", nread);
+ error = errno;
+ ATF_REQUIRE_EQ_MSG(error, ERESTART, "errno=%d (%s)", error,
+ strerror(error));
+
+ /*
+ * Now further attempts at I/O should fail with EBADF because
+ * the fd has been closed.
+ */
+ nread = rump_sys_read(F->fd, &c, sizeof(c));
+ ATF_REQUIRE_EQ_MSG(nread, -1, "nread=%zd", nread);
+ error = errno;
+ ATF_REQUIRE_EQ_MSG(error, EBADF, "errno=%d (%s)", error,
+ strerror(error));
+}
+
+static void
+dowrite(struct fdrestart *F)
+{
+ static const char buf[1024*1024]; /* XXX >BIG_PIPE_SIZE */
+ ssize_t nwrit;
+ int error;
+
+ /*
+ * Make sure the pipe's buffer is full first.
+ */
+ for (;;) {
+ int nspace;
+
+ RL(rump_sys_ioctl(F->fd, FIONSPACE, &nspace));
+ ATF_REQUIRE_MSG(nspace >= 0, "nspace=%d", nspace);
+ if (nspace == 0)
+ break;
+ RL(rump_sys_write(F->fd, buf, (size_t)nspace));
+ }
+
+ /*
+ * Wait for the other thread to be ready.
+ */
+ waitforbarrier(F, "writer");
+
+ /*
+ * Start a write. This should block, and then, when the other
+ * thread closes the fd, should be woken to fail with ERESTART.
+ */
+ nwrit = rump_sys_write(F->fd, buf, sizeof(buf));
+ ATF_REQUIRE_EQ_MSG(nwrit, -1, "nwrit=%zd", nwrit);
+ error = errno;
+ ATF_REQUIRE_EQ_MSG(error, ERESTART, "errno=%d (%s)", error,
+ strerror(error));
+
+ /*
+ * Now further attempts at I/O should fail with EBADF because
+ * the fd has been closed.
+ */
+ nwrit = rump_sys_write(F->fd, buf, sizeof(buf));
+ ATF_REQUIRE_EQ_MSG(nwrit, -1, "nwrit=%zd", nwrit);
+ error = errno;
+ ATF_REQUIRE_EQ_MSG(error, EBADF, "errno=%d (%s)", error,
+ strerror(error));
+}
+
+static void *
+doit(void *cookie)
+{
+ struct fdrestart *F = cookie;
+
+ (*F->op)(F);
+
+ return NULL;
+}
+
+static void
+on_sigalrm(int signo)
+{
+
+ atf_tc_fail("timed out");
+}
+
+static void
+testfdrestart(struct fdrestart *F)
+{
+ pthread_t t;
+
+ ATF_REQUIRE_MSG(signal(SIGALRM, &on_sigalrm) != SIG_ERR,
+ "errno=%d (%s)", errno, strerror(errno));
+
+ RZ(pthread_barrier_init(&F->barrier, NULL, 2));
+ RZ(pthread_create(&t, NULL, &doit, F));
+ waitforbarrier(F, "closer"); /* wait for thread to start */
+ (void)sleep(1); /* wait for op to start */
+ (void)alarm(1); /* set a deadline */
+ RL(rump_sys_close(F->fd)); /* wake op in other thread */
+ RZ(pthread_join(t, NULL)); /* wait for op to wake and fail */
+ (void)alarm(0); /* clear the deadline */
+}
+
+ATF_TC(pipe_read);
+ATF_TC_HEAD(pipe_read, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test pipe read fails on close");
+}
+ATF_TC_BODY(pipe_read, tc)
+{
+ struct fdrestart fdrestart, *F = &fdrestart;
+ int fd[2];
+
+ rump_init();
+
+ RL(rump_sys_pipe(fd));
+
+ memset(F, 0, sizeof(*F));
+ F->op = &doread;
+ F->fd = fd[0];
+ testfdrestart(F);
+}
+
+ATF_TC(pipe_write);
+ATF_TC_HEAD(pipe_write, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test pipe write fails on close");
+}
+ATF_TC_BODY(pipe_write, tc)
+{
+ struct fdrestart fdrestart, *F = &fdrestart;
+ int fd[2];
+
+ rump_init();
+
+ RL(rump_sys_pipe(fd));
+
+ memset(F, 0, sizeof(*F));
+ F->op = &dowrite;
+ F->fd = fd[1];
+ atf_tc_expect_fail("PR kern/57659");
+ testfdrestart(F);
+}
+
+ATF_TC(socketpair_read);
+ATF_TC_HEAD(socketpair_read, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test socketpair read fails on close");
+}
+ATF_TC_BODY(socketpair_read, tc)
+{
+ struct fdrestart fdrestart, *F = &fdrestart;
+ int fd[2];
+
+ rump_init();
+
+ RL(rump_sys_socketpair(AF_LOCAL, SOCK_STREAM, 0, fd));
+
+ memset(F, 0, sizeof(*F));
+ F->op = &doread;
+ F->fd = fd[0];
+ testfdrestart(F);
+}
+
+ATF_TC(socketpair_write);
+ATF_TC_HEAD(socketpair_write, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test socketpair write fails on close");
+}
+ATF_TC_BODY(socketpair_write, tc)
+{
+ struct fdrestart fdrestart, *F = &fdrestart;
+ int fd[2];
+
+ rump_init();
+
+ RL(rump_sys_socketpair(AF_LOCAL, SOCK_STREAM, 0, fd));
+
+ memset(F, 0, sizeof(*F));
+ F->op = &dowrite;
+ F->fd = fd[0];
+ testfdrestart(F);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, pipe_read);
+ ATF_TP_ADD_TC(tp, pipe_write);
+ ATF_TP_ADD_TC(tp, socketpair_read);
+ ATF_TP_ADD_TC(tp, socketpair_write);
+
+ return atf_no_error();
+}
diff --git a/kernel/t_nanosleep.c b/kernel/t_nanosleep.c
new file mode 100644
index 000000000000..9ef752b37e60
--- /dev/null
+++ b/kernel/t_nanosleep.c
@@ -0,0 +1,246 @@
+/* $NetBSD: t_nanosleep.c,v 1.2 2025/10/07 20:09:27 andvar Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__COPYRIGHT("@(#) Copyright (c) 2024\
+ The NetBSD Foundation, inc. All rights reserved.");
+__RCSID("$NetBSD: t_nanosleep.c,v 1.2 2025/10/07 20:09:27 andvar Exp $");
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+static void
+sacrifice(void)
+{
+ pause();
+}
+
+static void
+tester(pid_t victim, clockid_t clock, int flags)
+{
+ /*
+ * we need this sleep to be long enough that we
+ * can accurately detect when the sleep finishes
+ * early, but not so long that when there's no
+ * bug and things actually sleep this long, that
+ * the execution of a sleep this long, several
+ * times, won't slow down the overall testing
+ * process too much. Trial and error...
+ */
+ struct timespec to_sleep = { 4, 0 };
+
+ struct timespec before, after;
+ struct timespec *ts;
+ int e;
+
+ if (clock_gettime(clock, &before) != 0)
+ exit(1);
+
+ if (flags & TIMER_ABSTIME) {
+ timespecadd(&to_sleep, &before, &after);
+ ts = &after;
+ } else
+ ts = &to_sleep;
+
+ printf("Test: Clock=%d Flags=%x, starting at %jd.%.9ld\n",
+ (int)clock, flags, (intmax_t)before.tv_sec, before.tv_nsec);
+ if (flags & TIMER_ABSTIME)
+ printf("Sleeping until %jd.%.9ld\n",
+ (intmax_t)ts->tv_sec, ts->tv_nsec);
+ else
+ printf("Sleeping for %jd.%.9ld\n",
+ (intmax_t)ts->tv_sec, ts->tv_nsec);
+
+ /* OK, we're ready */
+
+ /* these next two steps need to be as close together as possible */
+ if (kill(victim, SIGKILL) == -1)
+ exit(2);
+ if ((e = clock_nanosleep(clock, flags, ts, &after)) != 0)
+ exit(20 + e);
+
+ if (!(flags & TIMER_ABSTIME)) {
+ printf("Remaining to sleep: %jd.%.9ld\n",
+ (intmax_t)after.tv_sec, after.tv_nsec);
+
+ if (after.tv_sec != 0 || after.tv_nsec != 0)
+ exit(3);
+ }
+
+ if (clock_gettime(clock, &after) != 0)
+ exit(4);
+
+ printf("Sleep ended at: %jd.%.9ld\n",
+ (intmax_t)after.tv_sec, after.tv_nsec);
+
+ timespecadd(&before, &to_sleep, &before);
+ if (timespeccmp(&before, &after, >))
+ exit(5);
+
+ exit(0);
+}
+
+/*
+ * The parent of the masochist/victim above, controls everything.
+ */
+static void
+runit(clockid_t clock, int flags)
+{
+ pid_t v, m, x;
+ int status;
+ struct timespec brief = { 0, 3 * 100 * 1000 * 1000 }; /* 300 ms */
+
+ ATF_REQUIRE((v = fork()) != -1);
+ if (v == 0)
+ sacrifice();
+
+ ATF_REQUIRE((m = fork()) != -1);
+ if (m == 0)
+ tester(v, clock, flags);
+
+ ATF_REQUIRE((x = wait(&status)) != -1);
+
+ if (x == m) {
+ /*
+ * This is bad, the murderer shouldn't die first
+ */
+ fprintf(stderr, "M exited first, status %#x\n", status);
+ (void)kill(v, SIGKILL); /* just in case */
+ atf_tc_fail("2nd child predeceased first");
+ }
+ if (x != v) {
+ fprintf(stderr, "Unknown exit from %d (status: %#x)"
+ "(M=%d V=%d)\n", x, status, m, v);
+ (void)kill(m, SIGKILL);
+ (void)kill(v, SIGKILL);
+ atf_tc_fail("Strange child died");
+ }
+
+ /*
+ * OK, the victim died, we don't really care why,
+ * (it should have been because of a SIGKILL, maybe
+ * test for that someday).
+ *
+ * Now we get to proceed to the real test.
+ *
+ * But we want to wait a short while to try and be sure
+ * that m (the child still running) has a chance to
+ * fall asleep.
+ */
+ (void) clock_nanosleep(CLOCK_MONOTONIC, TIMER_RELTIME, &brief, NULL);
+
+ /*
+ * This is the test, for PR kern/58733
+ * - stop a process while in clock_nanosleep()
+ * - resume it again
+ * - see if it still sleeps as long as was requested (or longer)
+ */
+ ATF_REQUIRE(kill(m, SIGSTOP) == 0);
+ (void) clock_nanosleep(CLOCK_MONOTONIC, TIMER_RELTIME, &brief, NULL);
+ ATF_REQUIRE(kill(m, SIGCONT) == 0);
+
+ ATF_REQUIRE((x = wait(&status)) != -1);
+
+ if (x != m) {
+ fprintf(stderr, "Unknown exit from %d (status: %#x)"
+ "(M=%d V=%d)\n", x, status, m, v);
+ (void) kill(m, SIGKILL);
+ atf_tc_fail("Strange child died");
+ }
+
+ if (status == 0)
+ atf_tc_pass();
+
+ /*
+ * Here we should decode the status, and give a better
+ * clue what really went wrong. Later...
+ */
+ fprintf(stderr, "Test failed: status from M: %#x\n", status);
+ atf_tc_fail("M exited with non-zero status. PR kern/58733");
+}
+
+
+ATF_TC(nanosleep_monotonic_absolute);
+ATF_TC_HEAD(nanosleep_monotonic_absolute, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks clock_nanosleep(MONO, ABS)");
+}
+ATF_TC_BODY(nanosleep_monotonic_absolute, tc)
+{
+ runit(CLOCK_MONOTONIC, TIMER_ABSTIME);
+}
+
+ATF_TC(nanosleep_monotonic_relative);
+ATF_TC_HEAD(nanosleep_monotonic_relative, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks clock_nanosleep(MONO, REL)");
+}
+ATF_TC_BODY(nanosleep_monotonic_relative, tc)
+{
+ runit(CLOCK_MONOTONIC, TIMER_RELTIME);
+}
+
+ATF_TC(nanosleep_realtime_absolute);
+ATF_TC_HEAD(nanosleep_realtime_absolute, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks clock_nanosleep(REAL, ABS)");
+}
+ATF_TC_BODY(nanosleep_realtime_absolute, tc)
+{
+ runit(CLOCK_REALTIME, TIMER_ABSTIME);
+}
+
+ATF_TC(nanosleep_realtime_relative);
+ATF_TC_HEAD(nanosleep_realtime_relative, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks clock_nanosleep(REAL, REL)");
+}
+ATF_TC_BODY(nanosleep_realtime_relative, tc)
+{
+ runit(CLOCK_REALTIME, TIMER_RELTIME);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, nanosleep_monotonic_absolute);
+ ATF_TP_ADD_TC(tp, nanosleep_monotonic_relative);
+ ATF_TP_ADD_TC(tp, nanosleep_realtime_absolute);
+ ATF_TP_ADD_TC(tp, nanosleep_realtime_relative);
+
+ return atf_no_error();
+}
diff --git a/kernel/t_semtimedop.c b/kernel/t_semtimedop.c
new file mode 100644
index 000000000000..daeaac6b3dab
--- /dev/null
+++ b/kernel/t_semtimedop.c
@@ -0,0 +1,279 @@
+/* $NetBSD: t_semtimedop.c,v 1.2 2024/10/10 07:45:02 martin Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_semtimedop.c,v 1.2 2024/10/10 07:45:02 martin Exp $");
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <atf-c.h>
+#include <sys/wait.h>
+
+union semun {
+ int val; /* value for SETVAL */
+ struct semid_ds *buf; /* buffer for IPC_{STAT,SET} */
+ u_short *array; /* array for GETALL & SETALL */
+};
+
+ATF_TC(semtimedop_basic);
+ATF_TC_HEAD(semtimedop_basic, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Basic semtimedop functionality");
+}
+
+ATF_TC_BODY(semtimedop_basic, tc)
+{
+ key_t key = IPC_PRIVATE;
+ int semid;
+ struct sembuf sops;
+ union semun sun;
+ struct timespec timeout;
+
+ /* Create semaphore set with 1 semaphore */
+ semid = semget(key, 1, IPC_CREAT | 0600);
+ ATF_REQUIRE_MSG(semid != -1, "semget failed: %s", strerror(errno));
+
+ /* Initialize semaphore to 0 */
+ sun.val = 0;
+ if (semctl(semid, 0, SETVAL, sun) == -1) {
+ ATF_REQUIRE_MSG(0, "semctl SETVAL failed: %s",
+ strerror(errno));
+ }
+
+ /* Define semtimedop operation: increment semaphore */
+ sops.sem_num = 0;
+ sops.sem_op = 1;
+ sops.sem_flg = 0;
+
+ /* Define timeout */
+ timeout.tv_sec = 1;
+ timeout.tv_nsec = 0;
+
+ /* Perform semtimedop */
+ if (semtimedop(semid, &sops, 1, &timeout) == -1) {
+ ATF_REQUIRE_MSG(0, "semtimedop failed: %s", strerror(errno));
+ }
+
+ /* Check semaphore value */
+ int val = semctl(semid, 0, GETVAL);
+ ATF_REQUIRE_MSG(val == 1,
+ "Semaphore value incorrect: got %d, expected 1", val);
+
+ /* Clean up */
+ ATF_REQUIRE_MSG(semctl(semid, 0, IPC_RMID) != -1,
+ "semctl IPC_RMID failed: %s", strerror(errno));
+}
+
+/* semtimedop blocks until timeout expires */
+ATF_TC(semtimedop_timeout);
+ATF_TC_HEAD(semtimedop_timeout, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "semtimedop blocks until timeout expires");
+}
+
+ATF_TC_BODY(semtimedop_timeout, tc)
+{
+ key_t key = IPC_PRIVATE;
+ int semid;
+ struct sembuf sops;
+ union semun sun;
+ struct timespec timeout;
+ pid_t pid;
+ int status;
+
+ /* Create semaphore set with 1 semaphore */
+ semid = semget(key, 1, IPC_CREAT | 0600);
+ ATF_REQUIRE_MSG(semid != -1, "semget failed: %s", strerror(errno));
+
+ /* Initialize semaphore to 0 */
+ sun.val = 0;
+ if (semctl(semid, 0, SETVAL, sun) == -1) {
+ ATF_REQUIRE_MSG(0, "semctl SETVAL failed: %s", strerror(errno));
+ }
+
+ pid = fork();
+ ATF_REQUIRE_MSG(pid != -1, "fork failed: %s", strerror(errno));
+
+ if (pid == 0) {
+ /*
+ * Child: perform semtimedop with negative sem_op, should
+ * block until timeout
+ */
+ sops.sem_num = 0;
+ sops.sem_op = -1;
+ sops.sem_flg = 0;
+
+ timeout.tv_sec = 2;
+ timeout.tv_nsec = 0;
+
+ if (semtimedop(semid, &sops, 1, &timeout) == -1) {
+ if (errno == EAGAIN || errno == EINTR) {
+ exit(0); /* Expected */
+ }
+ }
+ exit(1); /* Unexpected failure/success */
+ }
+
+ /* Parent: wait for child to finish */
+ waitpid(pid, &status, 0);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+ /* Clean up */
+ ATF_REQUIRE_MSG(semctl(semid, 0, IPC_RMID) != -1,
+ "semctl IPC_RMID failed: %s", strerror(errno));
+}
+
+/* semtimedop with SEM_UNDO adjusts semaphore on exit */
+ATF_TC(semtimedop_semundo);
+ATF_TC_HEAD(semtimedop_semundo, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "semtimedop with SEM_UNDO adjusts semaphore on exit");
+}
+
+ATF_TC_BODY(semtimedop_semundo, tc)
+{
+ key_t key = IPC_PRIVATE;
+ int semid;
+ struct sembuf sops;
+ union semun sun;
+ struct timespec timeout;
+ pid_t pid;
+ int val;
+
+ /* Create semaphore set with 1 semaphore */
+ semid = semget(key, 1, IPC_CREAT | 0600);
+ ATF_REQUIRE_MSG(semid != -1, "semget failed: %s", strerror(errno));
+
+ /* Initialize semaphore to 0 */
+ sun.val = 0;
+ if (semctl(semid, 0, SETVAL, sun) == -1) {
+ ATF_REQUIRE_MSG(0, "semctl SETVAL failed: %s", strerror(errno));
+ }
+
+ pid = fork();
+ ATF_REQUIRE_MSG(pid != -1, "fork failed: %s", strerror(errno));
+
+ if (pid == 0) {
+ /* Child: perform semtimedop with SEM_UNDO */
+ sops.sem_num = 0;
+ sops.sem_op = 1;
+ sops.sem_flg = SEM_UNDO;
+
+ timeout.tv_sec = 1;
+ timeout.tv_nsec = 0;
+
+ if (semtimedop(semid, &sops, 1, &timeout) == -1) {
+ exit(1); /* Unexpected failure */
+ }
+
+ exit(0); /* Exit normally, SEM_UNDO should be triggered */
+ }
+
+ /* Parent: wait for child to exit */
+ waitpid(pid, NULL, 0);
+
+ /* Check semaphore value; should be 0 after SEM_UNDO */
+ val = semctl(semid, 0, GETVAL);
+ ATF_REQUIRE_MSG(val == 0,
+ "Semaphore value incorrect after SEM_UNDO: got %d, "
+ "expected 0", val);
+
+ /* Clean up */
+ ATF_REQUIRE_MSG(semctl(semid, 0, IPC_RMID) != -1,
+ "semctl IPC_RMID failed: %s", strerror(errno));
+}
+
+/* semtimedop handles invalid parameters correctly */
+ATF_TC(semtimedop_invalid);
+ATF_TC_HEAD(semtimedop_invalid, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "semtimedop handles invalid parameters correctly");
+}
+
+ATF_TC_BODY(semtimedop_invalid, tc)
+{
+ struct sembuf sops;
+ union semun sun;
+ struct timespec timeout;
+
+ /* Invalid semaphore id */
+ sops.sem_num = 0;
+ sops.sem_op = -1;
+ sops.sem_flg = 0;
+
+ timeout.tv_sec = 1;
+ timeout.tv_nsec = 0;
+
+ /* Attempt to perform semtimedop on invalid semid */
+ ATF_REQUIRE_MSG(semtimedop(-1, &sops, 1, &timeout) == -1
+ && errno == EINVAL, "semtimedop did not fail on invalid semid");
+
+ /* Create semaphore set */
+ key_t key = IPC_PRIVATE;
+ int semid = semget(key, 1, IPC_CREAT | 0600);
+ ATF_REQUIRE_MSG(semid != -1, "semget failed: %s", strerror(errno));
+
+ /* Initialize semaphore to 0 */
+ sun.val = 0;
+ if (semctl(semid, 0, SETVAL, sun) == -1) {
+ ATF_REQUIRE_MSG(0, "semctl SETVAL failed: %s", strerror(errno));
+ }
+
+ /* Set an invalid semaphore number */
+ sops.sem_num = 1; /* Only 1 semaphore in set, index 0 */
+ sops.sem_op = 1;
+ sops.sem_flg = 0;
+
+ /* Attempt semtimedop with invalid sem_num */
+ ATF_REQUIRE_MSG(semtimedop(semid, &sops, 1, &timeout) == -1
+ && errno == EFBIG, "semtimedop did not fail on invalid sem_num");
+
+ /* Clean up */
+ ATF_REQUIRE_MSG(semctl(semid, 0, IPC_RMID) != -1,
+ "semctl IPC_RMID failed: %s", strerror(errno));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, semtimedop_basic);
+ ATF_TP_ADD_TC(tp, semtimedop_timeout);
+ ATF_TP_ADD_TC(tp, semtimedop_semundo);
+ ATF_TP_ADD_TC(tp, semtimedop_invalid);
+
+ return atf_no_error();
+}
diff --git a/kernel/t_setjmp.sh b/kernel/t_setjmp.sh
new file mode 100644
index 000000000000..1dbbd8504234
--- /dev/null
+++ b/kernel/t_setjmp.sh
@@ -0,0 +1,63 @@
+# $NetBSD: t_setjmp.sh,v 1.5 2025/04/29 10:57:17 martin Exp $
+#
+# Copyright (c) 2025 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+module_loaded=no
+atf_test_case setjmp cleanup
+setjmp_head()
+{
+ atf_set "descr" "Test setjmp(9)/longjmp(9)"
+}
+setjmp_body()
+{
+ case `uname -p` in
+ vax)
+ atf_expect_fail "PR port-vax/59308:" \
+ " kernel longjmp(9) fails to make setjmp(9) return 1"
+ ;;
+ esac
+
+ err=$( modstat -e 2>&1 )
+ if [ $? -gt 0 ]; then
+ atf_skip "${err##modstat:}"
+ fi
+
+ module_loaded="yes"
+ modload "$(atf_get_srcdir)/setjmp_tester/setjmp_tester.kmod"
+ atf_check -s exit:0 -o inline:'1\n' \
+ sysctl -n -w kern.setjmp_tester.test=1
+}
+setjmp_cleanup()
+{
+ if [ "${module_loaded}" != "no" ]; then
+ modunload setjmp_tester
+ fi
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case setjmp
+}
diff --git a/kernel/t_signal_and_sp.c b/kernel/t_signal_and_sp.c
new file mode 100644
index 000000000000..1cef56cd3041
--- /dev/null
+++ b/kernel/t_signal_and_sp.c
@@ -0,0 +1,628 @@
+/* $NetBSD: t_signal_and_sp.c,v 1.21 2025/04/26 23:49:55 uwe Exp $ */
+
+/*
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define __EXPOSE_STACK /* <sys/param.h>: expose STACK_ALIGNBYTES */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_signal_and_sp.c,v 1.21 2025/04/26 23:49:55 uwe Exp $");
+
+#include <sys/param.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <limits.h>
+#include <poll.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+#include "h_execsp.h"
+#include "h_macros.h"
+
+#ifdef HAVE_STACK_POINTER_H
+# include "stack_pointer.h"
+#endif
+
+#define PR_59327 "PR kern/59327: user stack pointer is not aligned properly"
+
+#ifdef HAVE_SIGNALSPHANDLER
+void signalsphandler(int); /* signalsphandler.S assembly routine */
+#endif
+
+void *volatile signalsp;
+
+static void
+test_execsp(const struct atf_tc *tc, const char *prog)
+{
+#ifdef STACK_ALIGNBYTES
+ char h_execsp[PATH_MAX];
+ struct execsp execsp;
+ int fd[2];
+ pid_t pid;
+ struct pollfd pollfd;
+ int nfds;
+ ssize_t nread;
+ int status;
+
+ /*
+ * Determine the full path to the helper program.
+ */
+ RL(snprintf(h_execsp, sizeof(h_execsp), "%s/%s",
+ atf_tc_get_config_var(tc, "srcdir"), prog));
+
+ /*
+ * Create a pipe to read a bundle of stack pointer samples from
+ * the child, and fork the child.
+ */
+ RL(pipe(fd));
+ RL(pid = vfork());
+ if (pid == 0) { /* child */
+ char *const argv[] = {h_execsp, NULL};
+
+ if (dup2(fd[1], STDOUT_FILENO) == -1)
+ _exit(1);
+ if (closefrom(STDERR_FILENO + 1) == -1)
+ _exit(2);
+ if (execve(argv[0], argv, NULL) == -1)
+ _exit(3);
+ _exit(4);
+ }
+
+ /*
+ * Close the writing end so, if something goes wrong in the
+ * child, we don't hang indefinitely waiting for output.
+ */
+ RL(close(fd[1]));
+
+ /*
+ * Wait up to 5sec for the child to return an answer. Any more
+ * than that, and we kill it. The child is mostly hand-written
+ * assembly routines where lots can go wrong, so don't bother
+ * waiting if it gets stuck in a loop.
+ */
+ pollfd.fd = fd[0];
+ pollfd.events = POLLIN;
+ RL(nfds = poll(&pollfd, 1, 5*1000/*ms*/));
+ if (nfds == 0) {
+ fprintf(stderr, "child hung, killing\n");
+ RL(kill(pid, SIGKILL));
+ }
+
+ /*
+ * Read a bundle of stack pointer samples from the child.
+ */
+ RL(nread = read(fd[0], &execsp, sizeof(execsp)));
+ ATF_CHECK_MSG((size_t)nread == sizeof(execsp),
+ "nread=%zu sizeof(execsp)=%zu",
+ (size_t)nread, sizeof(execsp));
+
+ /*
+ * Wait for the child to terminate and report failure if it
+ * didn't exit cleanly.
+ */
+ RL(waitpid(pid, &status, 0));
+ if (WIFSIGNALED(status)) {
+ atf_tc_fail_nonfatal("child exited on signal %d (%s)",
+ WTERMSIG(status), strsignal(WTERMSIG(status)));
+ } else if (!WIFEXITED(status)) {
+ atf_tc_fail_nonfatal("child exited status=0x%x", status);
+ } else {
+ ATF_CHECK_MSG(WEXITSTATUS(status) == 0,
+ "child exited with code %d",
+ WEXITSTATUS(status));
+ }
+
+ /*
+ * Now that we have reaped the child, stop here if the stack
+ * pointer samples are bogus; otherwise verify they are all
+ * aligned.
+ */
+ if ((size_t)nread != sizeof(execsp))
+ return; /* failed already */
+
+ printf("start sp @ %p\n", execsp.startsp);
+ printf("ctor sp @ %p\n", execsp.ctorsp);
+ printf("main sp @ %p\n", execsp.mainsp);
+ printf("dtor sp @ %p\n", execsp.dtorsp);
+
+ ATF_CHECK_MSG(((uintptr_t)execsp.startsp & STACK_ALIGNBYTES) == 0,
+ "elf entry point was called with misaligned sp: %p",
+ execsp.startsp);
+
+ ATF_CHECK_MSG(((uintptr_t)execsp.ctorsp & STACK_ALIGNBYTES) == 0,
+ "elf constructor was called with misaligned sp: %p",
+ execsp.ctorsp);
+
+ ATF_CHECK_MSG(((uintptr_t)execsp.mainsp & STACK_ALIGNBYTES) == 0,
+ "main function was called with misaligned sp: %p",
+ execsp.mainsp);
+
+ ATF_CHECK_MSG(((uintptr_t)execsp.dtorsp & STACK_ALIGNBYTES) == 0,
+ "elf destructor was called with misaligned sp: %p",
+ execsp.dtorsp);
+
+ /*
+ * Leave a reminder on architectures for which we haven't
+ * implemented execsp_start.S.
+ */
+ if (execsp.startsp == NULL ||
+ execsp.ctorsp == NULL ||
+ execsp.mainsp == NULL ||
+ execsp.dtorsp == NULL)
+ atf_tc_skip("Not fully supported on this architecture");
+#else
+ atf_tc_skip("Unknown STACK_ALIGNBYTES on this architecture");
+#endif
+}
+
+ATF_TC(execsp_dynamic);
+ATF_TC_HEAD(execsp_dynamic, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify stack pointer is aligned on dynamic program start");
+}
+ATF_TC_BODY(execsp_dynamic, tc)
+{
+ test_execsp(tc, "h_execsp_dynamic");
+}
+
+ATF_TC(execsp_static);
+ATF_TC_HEAD(execsp_static, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify stack pointer is aligned on static program start");
+}
+ATF_TC_BODY(execsp_static, tc)
+{
+ test_execsp(tc, "h_execsp_static");
+}
+
+#if defined STACK_ALIGNBYTES && defined HAVE_CONTEXTSPFUNC
+void *volatile contextsp; /* set by contextspfunc.S */
+static ucontext_t return_context;
+static volatile bool test_context_done;
+
+void contextspfunc(void); /* contextspfunc.S assembly routine */
+
+static void
+contextnoop(void)
+{
+
+ fprintf(stderr, "contextnoop\n");
+ /* control will return to contextspfunc via uc_link */
+}
+
+void contextdone(void); /* called by contextspfunc.S */
+void
+contextdone(void)
+{
+
+ fprintf(stderr, "contextdone\n");
+ ATF_REQUIRE(!test_context_done);
+ test_context_done = true;
+ RL(setcontext(&return_context));
+ atf_tc_fail("setcontext returned");
+}
+#endif
+
+ATF_TC(contextsp);
+ATF_TC_HEAD(contextsp, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify stack pointer is aligned on makecontext entry");
+}
+ATF_TC_BODY(contextsp, tc)
+{
+#if defined STACK_ALIGNBYTES && defined HAVE_CONTEXTSPFUNC
+ ucontext_t uc;
+ char *stack;
+ unsigned i;
+
+ REQUIRE_LIBC(stack = malloc(SIGSTKSZ + STACK_ALIGNBYTES), NULL);
+ fprintf(stderr, "stack @ [%p,%p)\n", stack,
+ stack + SIGSTKSZ + STACK_ALIGNBYTES);
+
+ for (i = 0; i <= STACK_ALIGNBYTES; i++) {
+ contextsp = NULL;
+ test_context_done = false;
+
+ RL(getcontext(&uc));
+ uc.uc_stack.ss_sp = stack;
+ uc.uc_stack.ss_size = SIGSTKSZ + i;
+ makecontext(&uc, &contextspfunc, 0);
+
+ fprintf(stderr, "[%u] swapcontext\n", i);
+ RL(swapcontext(&return_context, &uc));
+
+ ATF_CHECK(contextsp != NULL);
+ ATF_CHECK_MSG((uintptr_t)stack <= (uintptr_t)contextsp &&
+ (uintptr_t)contextsp <= (uintptr_t)stack + SIGSTKSZ + i,
+ "contextsp=%p", contextsp);
+ ATF_CHECK_MSG(((uintptr_t)contextsp & STACK_ALIGNBYTES) == 0,
+ "[%u] makecontext function called with misaligned sp %p",
+ i, contextsp);
+ }
+
+ for (i = 0; i <= STACK_ALIGNBYTES; i++) {
+ contextsp = NULL;
+ test_context_done = false;
+
+ RL(getcontext(&uc));
+ uc.uc_stack.ss_sp = stack + i;
+ uc.uc_stack.ss_size = SIGSTKSZ;
+ makecontext(&uc, &contextspfunc, 0);
+
+ fprintf(stderr, "[%u] swapcontext\n", i);
+ RL(swapcontext(&return_context, &uc));
+
+ ATF_CHECK(contextsp != NULL);
+ ATF_CHECK_MSG((uintptr_t)stack + i <= (uintptr_t)contextsp &&
+ (uintptr_t)contextsp <= (uintptr_t)stack + i + SIGSTKSZ,
+ "contextsp=%p", contextsp);
+ ATF_CHECK_MSG(((uintptr_t)contextsp & STACK_ALIGNBYTES) == 0,
+ "[%u] makecontext function called with misaligned sp %p",
+ i, contextsp);
+ }
+#else
+ atf_tc_skip("Not implemented on this platform");
+#endif
+}
+
+ATF_TC(contextsplink);
+ATF_TC_HEAD(contextsplink, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify stack pointer is aligned on makecontext link entry");
+}
+ATF_TC_BODY(contextsplink, tc)
+{
+#if defined STACK_ALIGNBYTES && defined HAVE_CONTEXTSPFUNC
+ ucontext_t uc1, uc2;
+ char *stack1, *stack2;
+ unsigned i;
+
+ REQUIRE_LIBC(stack1 = malloc(SIGSTKSZ), NULL);
+ fprintf(stderr, "stack1 @ [%p,%p)\n", stack1, stack1 + SIGSTKSZ);
+ REQUIRE_LIBC(stack2 = malloc(SIGSTKSZ + STACK_ALIGNBYTES), NULL);
+ fprintf(stderr, "stack2 @ [%p,%p)\n",
+ stack2, stack2 + SIGSTKSZ + STACK_ALIGNBYTES);
+
+ for (i = 0; i <= STACK_ALIGNBYTES; i++) {
+ contextsp = NULL;
+ test_context_done = false;
+
+ RL(getcontext(&uc1));
+ uc1.uc_stack.ss_sp = stack1;
+ uc1.uc_stack.ss_size = SIGSTKSZ;
+ uc1.uc_link = &uc2;
+ makecontext(&uc1, &contextnoop, 0);
+
+ RL(getcontext(&uc2));
+ uc2.uc_stack.ss_sp = stack2;
+ uc2.uc_stack.ss_size = SIGSTKSZ + i;
+ makecontext(&uc2, &contextspfunc, 0);
+
+ fprintf(stderr, "[%u] swapcontext\n", i);
+ RL(swapcontext(&return_context, &uc1));
+
+ ATF_CHECK(contextsp != NULL);
+ ATF_CHECK_MSG((uintptr_t)stack2 <= (uintptr_t)contextsp &&
+ (uintptr_t)contextsp <= (uintptr_t)stack2 + SIGSTKSZ + i,
+ "contextsp=%p", contextsp);
+ ATF_CHECK_MSG(((uintptr_t)contextsp & STACK_ALIGNBYTES) == 0,
+ "[%u] makecontext function called with misaligned sp %p",
+ i, contextsp);
+ }
+
+ for (i = 0; i <= STACK_ALIGNBYTES; i++) {
+ contextsp = NULL;
+ test_context_done = false;
+
+ RL(getcontext(&uc1));
+ uc1.uc_stack.ss_sp = stack1;
+ uc1.uc_stack.ss_size = SIGSTKSZ;
+ uc1.uc_link = &uc2;
+ makecontext(&uc1, &contextnoop, 0);
+
+ RL(getcontext(&uc2));
+ uc2.uc_stack.ss_sp = stack2 + i;
+ uc2.uc_stack.ss_size = SIGSTKSZ;
+ makecontext(&uc2, &contextspfunc, 0);
+
+ fprintf(stderr, "[%u] swapcontext\n", i);
+ RL(swapcontext(&return_context, &uc1));
+
+ ATF_CHECK(contextsp != NULL);
+ ATF_CHECK_MSG((uintptr_t)stack2 + i <= (uintptr_t)contextsp &&
+ (uintptr_t)contextsp <= (uintptr_t)stack2 + SIGSTKSZ + i,
+ "contextsp=%p", contextsp);
+ ATF_CHECK_MSG(((uintptr_t)contextsp & STACK_ALIGNBYTES) == 0,
+ "[%u] makecontext function called with misaligned sp %p",
+ i, contextsp);
+ }
+#else
+ atf_tc_skip("Not implemented on this platform");
+#endif
+}
+
+ATF_TC(signalsp);
+ATF_TC_HEAD(signalsp, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify stack pointer is aligned on entry to signal handler");
+}
+ATF_TC_BODY(signalsp, tc)
+{
+#if defined STACK_ALIGNBYTES && defined HAVE_SIGNALSPHANDLER
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = &signalsphandler;
+ RL(sigaction(SIGUSR1, &sa, NULL));
+ RL(raise(SIGUSR1));
+
+ ATF_CHECK_MSG(((uintptr_t)signalsp & STACK_ALIGNBYTES) == 0,
+ "signal handler was called with a misaligned sp: %p",
+ signalsp);
+#else
+ atf_tc_skip("Not implemented on this platform");
+#endif
+}
+
+ATF_TC(signalsp_sigaltstack);
+ATF_TC_HEAD(signalsp_sigaltstack, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify stack pointer is aligned on entry to signal handler"
+ " with maximally misaligned sigaltstack");
+}
+ATF_TC_BODY(signalsp_sigaltstack, tc)
+{
+#if defined STACK_ALIGNBYTES && HAVE_SIGNALSPHANDLER
+#if defined(__sh__)
+ atf_tc_expect_fail(PR_59327);
+#endif
+ char *stack;
+ struct sigaction sa;
+ struct sigaltstack ss;
+ unsigned i;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = &signalsphandler;
+ sa.sa_flags = SA_ONSTACK;
+ RL(sigaction(SIGUSR1, &sa, NULL));
+
+ /*
+ * Allocate a signal stack with enough slop to try all possible
+ * misalignments of the stack pointer. Print it to stderr so
+ * it always appears in atf output before shenanigans happen.
+ */
+ REQUIRE_LIBC(stack = malloc(SIGSTKSZ + STACK_ALIGNBYTES), NULL);
+ fprintf(stderr, "stack @ [%p, %p)\n",
+ stack, stack + SIGSTKSZ + STACK_ALIGNBYTES);
+
+ /*
+ * Try with all alignments of high addresses.
+ */
+ for (i = 0; i <= STACK_ALIGNBYTES; i++) {
+ ss.ss_sp = stack;
+ ss.ss_size = SIGSTKSZ + i;
+ ss.ss_flags = 0;
+ RL(sigaltstack(&ss, NULL));
+
+ signalsp = NULL;
+ RL(raise(SIGUSR1));
+ ATF_CHECK(signalsp != NULL);
+ ATF_CHECK_MSG((uintptr_t)stack <= (uintptr_t)signalsp &&
+ (uintptr_t)signalsp <= (uintptr_t)stack + SIGSTKSZ + i,
+ "signalsp=%p", signalsp);
+ ATF_CHECK_MSG(((uintptr_t)signalsp & STACK_ALIGNBYTES) == 0,
+ "[%u] signal handler was called with a misaligned sp: %p",
+ i, signalsp);
+ }
+
+ /*
+ * Try with all alignments of low addresses.
+ */
+ for (i = 0; i <= STACK_ALIGNBYTES; i++) {
+ ss.ss_sp = stack + i;
+ ss.ss_size = SIGSTKSZ;
+ ss.ss_flags = 0;
+ RL(sigaltstack(&ss, NULL));
+
+ signalsp = NULL;
+ RL(raise(SIGUSR1));
+ ATF_CHECK(signalsp != NULL);
+ ATF_CHECK_MSG((uintptr_t)stack + i <= (uintptr_t)signalsp &&
+ (uintptr_t)signalsp <= (uintptr_t)stack + i + SIGSTKSZ,
+ "signalsp=%p", signalsp);
+ ATF_CHECK_MSG(((uintptr_t)signalsp & STACK_ALIGNBYTES) == 0,
+ "[%u] signal handler was called with a misaligned sp: %p",
+ i, signalsp);
+ }
+#else
+ atf_tc_skip("Not implemented on this platform");
+#endif
+}
+
+#if defined STACK_ALIGNBYTES && defined HAVE_THREADSPFUNC
+void *threadspfunc(void *); /* threadspfunc.S assembly routine */
+#endif
+
+ATF_TC(threadsp);
+ATF_TC_HEAD(threadsp, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify stack pointer is aligned on thread start");
+}
+ATF_TC_BODY(threadsp, tc)
+{
+#if defined STACK_ALIGNBYTES && defined HAVE_THREADSPFUNC
+ pthread_t t;
+ void *sp;
+ char *stack;
+ unsigned i;
+
+ REQUIRE_LIBC(stack = malloc(SIGSTKSZ + STACK_ALIGNBYTES), NULL);
+ fprintf(stderr, "stack @ [%p,%p)\n", stack,
+ stack + SIGSTKSZ + STACK_ALIGNBYTES);
+
+ RZ(pthread_create(&t, NULL, &threadspfunc, NULL));
+
+ alarm(1);
+ RZ(pthread_join(t, &sp));
+ alarm(0);
+
+ ATF_CHECK(sp != NULL);
+ ATF_CHECK_MSG(((uintptr_t)sp & STACK_ALIGNBYTES) == 0,
+ "thread called with misaligned sp: %p", sp);
+
+ for (i = 0; i <= STACK_ALIGNBYTES; i++) {
+ pthread_attr_t attr;
+
+ RZ(pthread_attr_init(&attr));
+ RZ(pthread_attr_setstack(&attr, stack, SIGSTKSZ + i));
+ RZ(pthread_create(&t, &attr, &threadspfunc, NULL));
+ RZ(pthread_attr_destroy(&attr));
+
+ alarm(1);
+ RZ(pthread_join(t, &sp));
+ alarm(0);
+
+ ATF_CHECK(sp != NULL);
+ ATF_CHECK_MSG((uintptr_t)stack <= (uintptr_t)sp &&
+ (uintptr_t)sp <= (uintptr_t)stack + SIGSTKSZ + i,
+ "sp=%p", sp);
+ ATF_CHECK_MSG(((uintptr_t)sp & STACK_ALIGNBYTES) == 0,
+ "[%u] thread called with misaligned sp: %p", i, sp);
+ }
+
+ for (i = 0; i <= STACK_ALIGNBYTES; i++) {
+ pthread_attr_t attr;
+
+ RZ(pthread_attr_init(&attr));
+ RZ(pthread_attr_setstack(&attr, stack + i, SIGSTKSZ));
+ RZ(pthread_create(&t, &attr, &threadspfunc, NULL));
+ RZ(pthread_attr_destroy(&attr));
+
+ alarm(1);
+ RZ(pthread_join(t, &sp));
+ alarm(0);
+
+ ATF_CHECK(sp != NULL);
+ ATF_CHECK_MSG((uintptr_t)stack + i <= (uintptr_t)sp &&
+ (uintptr_t)sp <= (uintptr_t)stack + i + SIGSTKSZ,
+ "sp=%p", sp);
+ ATF_CHECK_MSG(((uintptr_t)sp & STACK_ALIGNBYTES) == 0,
+ "[%u] thread called with misaligned sp: %p", i, sp);
+ }
+#else
+ atf_tc_skip("Not implemented on this platform");
+#endif
+}
+
+ATF_TC(misaligned_sp_and_signal);
+ATF_TC_HEAD(misaligned_sp_and_signal, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "process can return from a signal"
+ " handler even if the stack pointer is misaligned when a signal"
+ " arrives");
+}
+ATF_TC_BODY(misaligned_sp_and_signal, tc)
+{
+#if defined STACK_ALIGNBYTES && defined HAVE_STACK_POINTER_H
+#if defined(__sh__)
+ atf_tc_expect_fail(PR_59327);
+#endif
+
+ /*
+ * Set up a handler for SIGALRM.
+ */
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = &signalsphandler;
+ RL(sigaction(SIGALRM, &sa, NULL));
+
+ /*
+ * Set up an interval timer so that we receive SIGALRM after 50 ms.
+ */
+ struct itimerval itv;
+ memset(&itv, 0, sizeof(itv));
+ itv.it_value.tv_usec = 1000 * 50;
+ RL(setitimer(ITIMER_MONOTONIC, &itv, NULL));
+
+ /*
+ * Now misalign the SP. Wait for the signal to arrive and see what
+ * happens. This should be fine as long as we don't use it to
+ * access memory.
+ */
+ MISALIGN_SP;
+ while (signalsp == NULL) {
+ /*
+ * Make sure the compiler does not optimize this busy loop
+ * away.
+ */
+ __asm__("" ::: "memory");
+ }
+ /*
+ * We could successfully return from a signal handler. Now we
+ * should fix the SP before calling any functions.
+ */
+ FIX_SP;
+
+ /*
+ * But was the stack pointer aligned when we were on the signal
+ * handler?
+ */
+ ATF_CHECK_MSG(((uintptr_t)signalsp & STACK_ALIGNBYTES) == 0,
+ "signal handler was called with a misaligned sp: %p",
+ signalsp);
+#else
+ atf_tc_skip("Not implemented for this platform");
+#endif
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, contextsp);
+ ATF_TP_ADD_TC(tp, contextsplink);
+ ATF_TP_ADD_TC(tp, execsp_dynamic);
+ ATF_TP_ADD_TC(tp, execsp_static);
+ ATF_TP_ADD_TC(tp, misaligned_sp_and_signal);
+ ATF_TP_ADD_TC(tp, signalsp);
+ ATF_TP_ADD_TC(tp, signalsp_sigaltstack);
+ ATF_TP_ADD_TC(tp, threadsp);
+ return atf_no_error();
+}
diff --git a/kernel/t_time_arith.c b/kernel/t_time_arith.c
new file mode 100644
index 000000000000..0f738bfbe19a
--- /dev/null
+++ b/kernel/t_time_arith.c
@@ -0,0 +1,1224 @@
+/* $NetBSD: t_time_arith.c,v 1.7 2025/10/06 12:05:04 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2024-2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_time_arith.c,v 1.7 2025/10/06 12:05:04 riastradh Exp $");
+
+#include <sys/timearith.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <limits.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "h_macros.h"
+
+enum { HZ = 100 };
+
+int hz = HZ;
+int tick = 1000000/HZ;
+
+static sig_atomic_t jmp_en;
+static int jmp_sig;
+static jmp_buf jmp;
+
+static void
+handle_signal(int signo)
+{
+ const int errno_save = errno;
+ char buf[32];
+
+ snprintf_ss(buf, sizeof(buf), "signal %d\n", signo);
+ (void)write(STDERR_FILENO, buf, strlen(buf));
+
+ errno = errno_save;
+
+ if (jmp_en) {
+ jmp_sig = signo;
+ jmp_en = 0;
+ longjmp(jmp, 1);
+ } else {
+ raise_default_signal(signo);
+ }
+}
+
+const struct itimer_transition {
+ struct itimerspec it_time;
+ struct timespec it_now;
+ struct timespec it_next;
+ int it_overruns;
+ const char *it_xfail;
+} itimer_transitions[] = {
+ /*
+ * Fired more than one interval early -- treat clock as wound
+ * backwards, not counting overruns. Advance to the next
+ * integral multiple of it_interval starting from it_value.
+ */
+ [0] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {0,1}, {1,0}, 0,
+ NULL},
+ [1] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {0,500000000}, {1,0}, 0,
+ NULL},
+ [2] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {0,999999999}, {1,0}, 0,
+ NULL},
+ [3] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {1,0}, {2,0}, 0,
+ NULL},
+ [4] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {1,1}, {2,0}, 0,
+ NULL},
+ [5] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {1,500000000}, {2,0}, 0,
+ NULL},
+ [6] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {1,999999999}, {2,0}, 0,
+ NULL},
+
+ /*
+ * Fired exactly one interval early. Treat this too as clock
+ * wound backwards.
+ */
+ [7] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {2,0}, {3,0}, 0,
+ NULL},
+
+ /*
+ * Fired less than one interval early -- callouts and real-time
+ * clock might not be perfectly synced, counted as zero
+ * overruns. Advance by one interval from the scheduled time.
+ */
+ [8] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {2,1}, {3,0}, 0,
+ NULL},
+ [9] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {2,500000000}, {3,0}, 0,
+ NULL},
+ [10] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {2,999999999}, {3,0}, 0,
+ NULL},
+
+ /*
+ * Fired exactly on time. Advance by one interval.
+ */
+ [11] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {3,0}, {4,0}, 0, NULL},
+
+ /*
+ * Fired late by less than one interval -- callouts and
+ * real-time clock might not be prefectly synced, counted as
+ * zero overruns. Advance by one interval from the scheduled
+ * time (even if it's very close to a full interval).
+ */
+ [12] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {3,1}, {4,0}, 0, NULL},
+ [14] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {3,500000000}, {4,0}, 0, NULL},
+ [15] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {3,999999999}, {4,0}, 0, NULL},
+
+ /*
+ * Fired late by exactly one interval -- treat it as overrun.
+ */
+ [16] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {4,0}, {5,0}, 1,
+ NULL},
+
+ /*
+ * Fired late by more than one interval but less than two --
+ * overrun.
+ */
+ [17] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {4,1}, {5,0}, 1,
+ NULL},
+ [18] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {4,500000000}, {5,0}, 1,
+ NULL},
+ [19] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {4,999999999}, {5,0}, 1,
+ NULL},
+
+ /*
+ * Fired late by exactly two intervals -- two overruns.
+ */
+ [20] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {5,0}, {6,0}, 2,
+ NULL},
+
+ /*
+ * Fired late by more intervals plus slop, up to 32.
+ *
+ * XXX Define DELAYTIMER_MAX so we can write it in terms of
+ * that.
+ */
+ [21] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {13,123456789}, {14,0}, 10,
+ NULL},
+ [22] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {34,999999999}, {35,0}, 31,
+ NULL},
+
+ /*
+ * Fired late by roughly INT_MAX intervals.
+ */
+ [23] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {(time_t)3 + INT_MAX - 1, 0},
+ {(time_t)3 + INT_MAX, 0},
+ INT_MAX - 1,
+ NULL},
+ [24] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {(time_t)3 + INT_MAX, 0},
+ {(time_t)3 + INT_MAX + 1, 0},
+ INT_MAX,
+ NULL},
+ [25] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {(time_t)3 + INT_MAX + 1, 0},
+ {(time_t)3 + INT_MAX + 2, 0},
+ INT_MAX,
+ NULL},
+
+ /* (2^63 - 1) ns */
+ [26] = {{.it_value = {3,0}, .it_interval = {9223372036,854775807}},
+ {3,1}, {9223372039,854775807}, 0, NULL},
+ /* 2^63 ns */
+ [27] = {{.it_value = {3,0}, .it_interval = {9223372036,854775808}},
+ {3,1}, {9223372039,854775808}, 0, NULL},
+ /* (2^63 + 1) ns */
+ [28] = {{.it_value = {3,0}, .it_interval = {9223372036,854775809}},
+ {3,1}, {9223372039,854775809}, 0, NULL},
+
+ /*
+ * Overflows -- we should (XXX but currently don't) reject
+ * intervals of at least 2^64 nanoseconds up front, since this
+ * is more time than it is reasonable to wait (more than 584
+ * years).
+ */
+
+ /* (2^64 - 1) ns */
+ [29] = {{.it_value = {3,0}, .it_interval = {18446744073,709551615}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* 2^64 ns */
+ [30] = {{.it_value = {3,0}, .it_interval = {18446744073,709551616}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* (2^64 + 1) ns */
+ [31] = {{.it_value = {3,0}, .it_interval = {18446744073,709551617}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+
+ /* (2^63 - 1) us */
+ [32] = {{.it_value = {3,0}, .it_interval = {9223372036854,775807}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* 2^63 us */
+ [33] = {{.it_value = {3,0}, .it_interval = {9223372036854,775808}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* (2^63 + 1) us */
+ [34] = {{.it_value = {3,0}, .it_interval = {9223372036854,775809}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+
+ /* (2^64 - 1) us */
+ [35] = {{.it_value = {3,0}, .it_interval = {18446744073709,551615}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* 2^64 us */
+ [36] = {{.it_value = {3,0}, .it_interval = {18446744073709,551616}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* (2^64 + 1) us */
+ [37] = {{.it_value = {3,0}, .it_interval = {18446744073709,551617}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+
+ /* (2^63 - 1) ms */
+ [38] = {{.it_value = {3,0}, .it_interval = {9223372036854775,807}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* 2^63 ms */
+ [39] = {{.it_value = {3,0}, .it_interval = {9223372036854775,808}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* (2^63 + 1) ms */
+ [40] = {{.it_value = {3,0}, .it_interval = {9223372036854775,809}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+
+ /* (2^64 - 1) ms */
+ [41] = {{.it_value = {3,0}, .it_interval = {18446744073709551,615}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* 2^64 ms */
+ [42] = {{.it_value = {3,0}, .it_interval = {18446744073709551,616}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* (2^64 + 1) ms */
+ [43] = {{.it_value = {3,0}, .it_interval = {18446744073709551,617}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+
+ /* invalid intervals */
+ [44] = {{.it_value = {3,0}, .it_interval = {-1,0}},
+ {3,1}, {0,0}, 0, NULL},
+ [45] = {{.it_value = {3,0}, .it_interval = {0,-1}},
+ {3,1}, {0,0}, 0, NULL},
+ [46] = {{.it_value = {3,0}, .it_interval = {0,1000000000}},
+ {3,1}, {0,0}, 0, NULL},
+
+ /*
+ * Overflow nanosecond arithmetic. The magic interval number
+ * here is ceiling(INT64_MAX/2) nanoseconds. The interval
+ * start value will be rounded to an integral number of ticks,
+ * so rather than write exactly `4611686018,427387905', just
+ * round up the `now' value to the next second. This forces an
+ * overrun _and_ triggers int64_t arithmetic overflow.
+ */
+ [47] = {{.it_value = {0,1},
+ .it_interval = {4611686018,427387904}},
+ /* XXX needless overflow */
+ {4611686019,0}, {0,0}, 1,
+ NULL},
+
+ /* interval ~ 1/4 * (2^63 - 1) ns, now ~ 3/4 * (2^63 - 1) ns */
+ [48] = {{.it_value = {0,1},
+ .it_interval = {2305843009,213693952}},
+ /* XXX needless overflow */
+ {6917529028,0}, {0,0}, 3,
+ NULL},
+ [49] = {{.it_value = {6917529027,0},
+ .it_interval = {2305843009,213693952}},
+ {6917529028,0}, {9223372036,213693952}, 0, NULL},
+ [50] = {{.it_value = {6917529029,0},
+ .it_interval = {2305843009,213693952}},
+ {6917529028,0}, {6917529029,0}, 0,
+ NULL},
+
+ /* interval ~ 1/2 * (2^63 - 1) ns, now ~ 3/4 * (2^63 - 1) ns */
+ [51] = {{.it_value = {0,1},
+ .it_interval = {4611686018,427387904}},
+ /* XXX needless overflow */
+ {6917529028,0}, {0,0}, 1,
+ NULL},
+ [52] = {{.it_value = {2305843009,213693951}, /* ~1/4 * (2^63 - 1) */
+ .it_interval = {4611686018,427387904}},
+ /* XXX needless overflow */
+ {6917529028,0}, {0,0}, 1,
+ NULL},
+ [54] = {{.it_value = {6917529027,0},
+ .it_interval = {4611686018,427387904}},
+ {6917529028,0}, {11529215045,427387904}, 0, NULL},
+ [55] = {{.it_value = {6917529029,0},
+ .it_interval = {4611686018,427387904}},
+ {6917529028,0}, {6917529029,0}, 0,
+ NULL},
+
+ [56] = {{.it_value = {INT64_MAX - 1,0}, .it_interval = {1,0}},
+ /* XXX needless overflow */
+ {INT64_MAX - 2,999999999}, {0,0}, 0,
+ NULL},
+ [57] = {{.it_value = {INT64_MAX - 1,0}, .it_interval = {1,0}},
+ {INT64_MAX - 1,0}, {INT64_MAX,0}, 0, NULL},
+ [58] = {{.it_value = {INT64_MAX - 1,0}, .it_interval = {1,0}},
+ {INT64_MAX - 1,1}, {INT64_MAX,0}, 0, NULL},
+ [59] = {{.it_value = {INT64_MAX - 1,0}, .it_interval = {1,0}},
+ {INT64_MAX - 1,999999999}, {INT64_MAX,0}, 0, NULL},
+ [60] = {{.it_value = {INT64_MAX - 1,0}, .it_interval = {1,0}},
+ {INT64_MAX,0}, {0,0}, 0,
+ NULL},
+ [61] = {{.it_value = {INT64_MAX - 1,0}, .it_interval = {1,0}},
+ {INT64_MAX,1}, {0,0}, 0,
+ NULL},
+ [62] = {{.it_value = {INT64_MAX - 1,0}, .it_interval = {1,0}},
+ {INT64_MAX,999999999}, {0,0}, 0,
+ NULL},
+
+ [63] = {{.it_value = {INT64_MAX,0}, .it_interval = {1,0}},
+ {INT64_MAX - 1,1}, {0,0}, 0,
+ NULL},
+ [64] = {{.it_value = {INT64_MAX,0}, .it_interval = {1,0}},
+ {INT64_MAX - 1,999999999}, {0,0}, 0,
+ NULL},
+ [65] = {{.it_value = {INT64_MAX,0}, .it_interval = {1,0}},
+ {INT64_MAX,0}, {0,0}, 0,
+ NULL},
+ [66] = {{.it_value = {INT64_MAX,0}, .it_interval = {1,0}},
+ {INT64_MAX,1}, {0,0}, 0,
+ NULL},
+ [67] = {{.it_value = {INT64_MAX,0}, .it_interval = {1,0}},
+ {INT64_MAX,999999999}, {0,0}, 0,
+ NULL},
+};
+
+ATF_TC(itimer_transitions);
+ATF_TC_HEAD(itimer_transitions, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Tests interval timer transitions");
+}
+ATF_TC_BODY(itimer_transitions, tc)
+{
+ volatile unsigned i;
+
+ REQUIRE_LIBC(signal(SIGFPE, handle_signal), SIG_ERR);
+ REQUIRE_LIBC(signal(SIGABRT, handle_signal), SIG_ERR);
+
+ for (i = 0; i < __arraycount(itimer_transitions); i++) {
+ struct itimer_transition it = itimer_transitions[i];
+ struct timespec next;
+ int overruns;
+ volatile bool aborted = true;
+ volatile bool expect_abort = false;
+
+ fprintf(stderr, "case %u\n", i);
+
+ if (it.it_xfail)
+ atf_tc_expect_fail("%s", it.it_xfail);
+
+ if (itimespecfix(&it.it_time.it_value) != 0 ||
+ itimespecfix(&it.it_time.it_interval) != 0) {
+ fprintf(stderr, "rejected by itimerspecfix\n");
+ expect_abort = true;
+ }
+
+ if (setjmp(jmp) == 0) {
+ jmp_en = 1;
+ itimer_transition(&it.it_time, &it.it_now,
+ &next, &overruns);
+ jmp_en = 0;
+ aborted = false;
+ }
+ ATF_CHECK(!jmp_en);
+ jmp_en = 0; /* paranoia */
+ if (expect_abort) {
+ fprintf(stderr, "expected abort\n");
+ ATF_CHECK_MSG(aborted,
+ "[%u] missing invariant assertion", i);
+ ATF_CHECK_MSG(jmp_sig == SIGABRT,
+ "[%u] missing invariant assertion", i);
+ } else {
+ ATF_CHECK_MSG(!aborted, "[%u] raised signal %d: %s", i,
+ jmp_sig, strsignal(jmp_sig));
+ }
+
+ ATF_CHECK_MSG((next.tv_sec == it.it_next.tv_sec &&
+ next.tv_nsec == it.it_next.tv_nsec),
+ "[%u] periodic intervals of %lld.%09d from %lld.%09d"
+ " last expired at %lld.%09d:"
+ " next expiry at %lld.%09d, expected %lld.%09d", i,
+ (long long)it.it_time.it_interval.tv_sec,
+ (int)it.it_time.it_interval.tv_nsec,
+ (long long)it.it_time.it_value.tv_sec,
+ (int)it.it_time.it_value.tv_nsec,
+ (long long)it.it_now.tv_sec, (int)it.it_now.tv_nsec,
+ (long long)next.tv_sec, (int)next.tv_nsec,
+ (long long)it.it_next.tv_sec, (int)it.it_next.tv_nsec);
+ ATF_CHECK_EQ_MSG(overruns, it.it_overruns,
+ "[%u] periodic intervals of %lld.%09d from %lld.%09d"
+ " last expired at %lld.%09d:"
+ " overruns %d, expected %d", i,
+ (long long)it.it_time.it_interval.tv_sec,
+ (int)it.it_time.it_interval.tv_nsec,
+ (long long)it.it_time.it_value.tv_sec,
+ (int)it.it_time.it_value.tv_nsec,
+ (long long)it.it_now.tv_sec, (int)it.it_now.tv_nsec,
+ overruns, it.it_overruns);
+
+ if (it.it_xfail)
+ atf_tc_expect_pass();
+ }
+}
+
+/*
+ * { 0, if t <= 0;
+ * tstohz(t sec) @ f Hz = { ceil(t/(1/f)), if that's below INT_MAX;
+ * { INT_MAX, otherwise.
+ */
+
+#define TSTOHZ_ROUND_XFAIL \
+ "PR kern/59691: tstohz(9) rounding errors"
+
+const struct tstohz_case {
+ int ts_hz;
+ struct timespec ts_ts;
+ int ts_ticks;
+ const char *ts_xfail;
+} tstohz_cases[] = {
+ /*
+ * hz = 10
+ */
+
+ /* negative inputs yield 0 ticks */
+ [0] = {10, {.tv_sec = -1, .tv_nsec = 0}, 0, NULL},
+ [1] = {10, {.tv_sec = -1, .tv_nsec = 999999999}, 0, NULL},
+
+ /* zero input yields 0 ticks */
+ [2] = {10, {.tv_sec = 0, .tv_nsec = 0}, 0, NULL},
+
+ /*
+ * Nonzero input always yields >=2 ticks, because the time from
+ * now until the next tick may be arbitrarily short, and we
+ * need to wait one full tick, so we have to wait for two
+ * ticks.
+ */
+ [3] = {10, {.tv_sec = 0, .tv_nsec = 1}, 2, NULL},
+ [4] = {10, {.tv_sec = 0, .tv_nsec = 2}, 2, NULL},
+ [5] = {10, {.tv_sec = 0, .tv_nsec = 99999999}, 2, NULL},
+ [6] = {10, {.tv_sec = 0, .tv_nsec = 100000000}, 2, NULL},
+ [7] = {10, {.tv_sec = 0, .tv_nsec = 100000001}, 3, NULL},
+ [8] = {10, {.tv_sec = 0, .tv_nsec = 100000002}, 3, NULL},
+ [9] = {10, {.tv_sec = 0, .tv_nsec = 199999999}, 3, NULL},
+ [10] = {10, {.tv_sec = 0, .tv_nsec = 200000000}, 3, NULL},
+ [11] = {10, {.tv_sec = 0, .tv_nsec = 200000001}, 4, NULL},
+ [12] = {10, {.tv_sec = 0, .tv_nsec = 200000002}, 4, NULL},
+ [13] = {10, {.tv_sec = 0, .tv_nsec = 999999999}, 11, NULL},
+ [14] = {10, {.tv_sec = 1, .tv_nsec = 0}, 11, NULL},
+ [15] = {10, {.tv_sec = 1, .tv_nsec = 1}, 12, NULL},
+ [16] = {10, {.tv_sec = 1, .tv_nsec = 2}, 12, NULL},
+ /* .tv_sec ~ INT32_MAX/1000000 */
+ [17] = {10, {.tv_sec = 2147, .tv_nsec = 999999999}, 21481, NULL},
+ [18] = {10, {.tv_sec = 2148, .tv_nsec = 0}, 21481, NULL},
+ [19] = {10, {.tv_sec = 2148, .tv_nsec = 1}, 21482, NULL},
+ [20] = {10, {.tv_sec = 2148, .tv_nsec = 2}, 21482, NULL},
+ /* .tv_sec ~ INT32_MAX/hz */
+ [21] = {10, {.tv_sec = 214748364, .tv_nsec = 499999999}, 2147483646,
+ NULL},
+ /* saturate at INT_MAX = 2^31 - 1 ticks */
+ [22] = {10, {.tv_sec = 214748364, .tv_nsec = 500000000}, 2147483646,
+ NULL},
+ [23] = {10, {.tv_sec = 214748364, .tv_nsec = 500000001}, 2147483647,
+ NULL},
+ [24] = {10, {.tv_sec = 214748364, .tv_nsec = 500000002}, 2147483647,
+ NULL},
+ [25] = {10, {.tv_sec = 214748364, .tv_nsec = 599999999}, 2147483647,
+ NULL},
+ [26] = {10, {.tv_sec = 214748364, .tv_nsec = 600000000}, 2147483647,
+ NULL},
+ [27] = {10, {.tv_sec = 214748364, .tv_nsec = 999999999}, 2147483647,
+ NULL},
+ [28] = {10, {.tv_sec = 214748365, .tv_nsec = 0}, 2147483647,
+ NULL},
+ [29] = {10, {.tv_sec = 214748365, .tv_nsec = 1}, 2147483647,
+ NULL},
+ [30] = {10, {.tv_sec = 214748365, .tv_nsec = 2}, 2147483647,
+ NULL},
+ [31] = {10, {.tv_sec = (time_t)INT_MAX + 1, .tv_nsec = 123456789},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/1000000, overflows to INT_MAX ticks */
+ [32] = {10, {.tv_sec = 9223372036854, .tv_nsec = 999999999},
+ INT_MAX, NULL},
+ [33] = {10, {.tv_sec = 9223372036855, .tv_nsec = 0},
+ INT_MAX, NULL},
+ [34] = {10, {.tv_sec = 9223372036855, .tv_nsec = 1},
+ INT_MAX, NULL},
+ [35] = {10, {.tv_sec = 9223372036855, .tv_nsec = 2},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/hz, overflows to INT_MAX ticks */
+ [36] = {10, {.tv_sec = 922337203685477580, .tv_nsec = 999999999},
+ INT_MAX, NULL},
+ [37] = {10, {.tv_sec = 922337203685477581, .tv_nsec = 0},
+ INT_MAX, NULL},
+ [38] = {10, {.tv_sec = 922337203685477581, .tv_nsec = 1},
+ INT_MAX, NULL},
+ [39] = {10, {.tv_sec = 922337203685477581, .tv_nsec = 2},
+ INT_MAX, NULL},
+ [40] = {10, {.tv_sec = (time_t)INT_MAX + 1, .tv_nsec = 123456789},
+ INT_MAX, NULL},
+
+ /*
+ * hz = 100
+ */
+
+ [41] = {100, {.tv_sec = -1, .tv_nsec = 0}, 0, NULL},
+ [42] = {100, {.tv_sec = -1, .tv_nsec = 999999999}, 0, NULL},
+ [43] = {100, {.tv_sec = 0, .tv_nsec = 0}, 0, NULL},
+ [44] = {100, {.tv_sec = 0, .tv_nsec = 1}, 2, NULL},
+ [45] = {100, {.tv_sec = 0, .tv_nsec = 2}, 2, NULL},
+ [46] = {100, {.tv_sec = 0, .tv_nsec = 9999999}, 2, NULL},
+ [47] = {100, {.tv_sec = 0, .tv_nsec = 10000000}, 2, NULL},
+ [48] = {100, {.tv_sec = 0, .tv_nsec = 10000001}, 3, NULL},
+ [49] = {100, {.tv_sec = 0, .tv_nsec = 10000002}, 3, NULL},
+ [50] = {100, {.tv_sec = 0, .tv_nsec = 19999999}, 3, NULL},
+ [51] = {100, {.tv_sec = 0, .tv_nsec = 20000000}, 3, NULL},
+ [52] = {100, {.tv_sec = 0, .tv_nsec = 20000001}, 4, NULL},
+ [53] = {100, {.tv_sec = 0, .tv_nsec = 20000002}, 4, NULL},
+ [54] = {100, {.tv_sec = 0, .tv_nsec = 99999999}, 11, NULL},
+ [55] = {100, {.tv_sec = 0, .tv_nsec = 100000000}, 11, NULL},
+ [56] = {100, {.tv_sec = 0, .tv_nsec = 100000001}, 12, NULL},
+ [57] = {100, {.tv_sec = 0, .tv_nsec = 100000002}, 12, NULL},
+ [58] = {100, {.tv_sec = 0, .tv_nsec = 999999999}, 101, NULL},
+ [59] = {100, {.tv_sec = 1, .tv_nsec = 0}, 101, NULL},
+ [60] = {100, {.tv_sec = 1, .tv_nsec = 1}, 102, NULL},
+ [61] = {100, {.tv_sec = 1, .tv_nsec = 2}, 102, NULL},
+ /* .tv_sec ~ INT32_MAX/1000000 */
+ [62] = {100, {.tv_sec = 2147, .tv_nsec = 999999999}, 214801, NULL},
+ [63] = {100, {.tv_sec = 2148, .tv_nsec = 0}, 214801, NULL},
+ [64] = {100, {.tv_sec = 2148, .tv_nsec = 1}, 214802, NULL},
+ [65] = {100, {.tv_sec = 2148, .tv_nsec = 2}, 214802, NULL},
+ /* .tv_sec ~ INT32_MAX/hz */
+ [66] = {100, {.tv_sec = 21474836, .tv_nsec = 439999999}, 2147483645,
+ NULL},
+ [67] = {100, {.tv_sec = 21474836, .tv_nsec = 440000000}, 2147483645,
+ NULL},
+ [68] = {100, {.tv_sec = 21474836, .tv_nsec = 440000001}, 2147483646,
+ NULL},
+ [69] = {100, {.tv_sec = 21474836, .tv_nsec = 440000002}, 2147483646,
+ NULL},
+ [70] = {100, {.tv_sec = 21474836, .tv_nsec = 449999999}, 2147483646,
+ NULL},
+ [71] = {100, {.tv_sec = 21474836, .tv_nsec = 450000000}, 2147483646,
+ NULL},
+ /* saturate at INT_MAX = 2^31 - 1 ticks */
+ [72] = {100, {.tv_sec = 21474836, .tv_nsec = 450000001}, 2147483647,
+ NULL},
+ [73] = {100, {.tv_sec = 21474836, .tv_nsec = 450000002}, 2147483647,
+ NULL},
+ [74] = {100, {.tv_sec = 21474836, .tv_nsec = 459999999}, 2147483647,
+ NULL},
+ [75] = {100, {.tv_sec = 21474836, .tv_nsec = 460000000}, 2147483647,
+ NULL},
+ [76] = {100, {.tv_sec = 21474836, .tv_nsec = 460000001}, 2147483647,
+ NULL},
+ [77] = {100, {.tv_sec = 21474836, .tv_nsec = 460000002}, 2147483647,
+ NULL},
+ [78] = {100, {.tv_sec = 21474836, .tv_nsec = 999999999}, 2147483647,
+ NULL},
+ [79] = {100, {.tv_sec = 21474837, .tv_nsec = 0}, 2147483647,
+ NULL},
+ [80] = {100, {.tv_sec = 21474837, .tv_nsec = 1}, 2147483647,
+ NULL},
+ [81] = {100, {.tv_sec = 21474837, .tv_nsec = 2}, 2147483647,
+ NULL},
+ [82] = {100, {.tv_sec = 21474837, .tv_nsec = 2}, 2147483647,
+ NULL},
+ /* .tv_sec ~ INT64_MAX/1000000 */
+ [83] = {100, {.tv_sec = 9223372036854, .tv_nsec = 999999999},
+ INT_MAX, NULL},
+ [84] = {100, {.tv_sec = 9223372036855, .tv_nsec = 0},
+ INT_MAX, NULL},
+ [85] = {100, {.tv_sec = 9223372036855, .tv_nsec = 1},
+ INT_MAX, NULL},
+ [86] = {100, {.tv_sec = 9223372036855, .tv_nsec = 2},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/hz, overflows to INT_MAX ticks */
+ [87] = {100, {.tv_sec = 92233720368547758, .tv_nsec = 999999999},
+ INT_MAX, NULL},
+ [88] = {100, {.tv_sec = 92233720368547758, .tv_nsec = 0},
+ INT_MAX, NULL},
+ [89] = {100, {.tv_sec = 92233720368547758, .tv_nsec = 1},
+ INT_MAX, NULL},
+ [90] = {100, {.tv_sec = 92233720368547758, .tv_nsec = 2},
+ INT_MAX, NULL},
+ [91] = {100, {.tv_sec = (time_t)INT_MAX + 1, .tv_nsec = 123456789},
+ INT_MAX, NULL},
+
+ /*
+ * hz = 1000
+ */
+
+ [92] = {1000, {.tv_sec = -1, .tv_nsec = 0}, 0, NULL},
+ [93] = {1000, {.tv_sec = -1, .tv_nsec = 999999999}, 0, NULL},
+ [94] = {1000, {.tv_sec = 0, .tv_nsec = 0}, 0, NULL},
+ [95] = {1000, {.tv_sec = 0, .tv_nsec = 1}, 2, NULL},
+ [96] = {1000, {.tv_sec = 0, .tv_nsec = 2}, 2, NULL},
+ [97] = {1000, {.tv_sec = 0, .tv_nsec = 999999}, 2, NULL},
+ [98] = {1000, {.tv_sec = 0, .tv_nsec = 1000000}, 2, NULL},
+ [99] = {1000, {.tv_sec = 0, .tv_nsec = 1000001}, 3, NULL},
+ [100] = {1000, {.tv_sec = 0, .tv_nsec = 1000002}, 3, NULL},
+ [101] = {1000, {.tv_sec = 0, .tv_nsec = 1999999}, 3, NULL},
+ [102] = {1000, {.tv_sec = 0, .tv_nsec = 2000000}, 3, NULL},
+ [103] = {1000, {.tv_sec = 0, .tv_nsec = 2000001}, 4, NULL},
+ [104] = {1000, {.tv_sec = 0, .tv_nsec = 2000002}, 4, NULL},
+ [105] = {1000, {.tv_sec = 0, .tv_nsec = 999999999}, 1001, NULL},
+ [106] = {1000, {.tv_sec = 1, .tv_nsec = 0}, 1001, NULL},
+ [107] = {1000, {.tv_sec = 1, .tv_nsec = 1}, 1002, NULL},
+ [108] = {1000, {.tv_sec = 1, .tv_nsec = 2}, 1002, NULL},
+ /* .tv_sec ~ INT_MAX/1000000 */
+ [109] = {1000, {.tv_sec = 2147, .tv_nsec = 999999999}, 2148001, NULL},
+ [110] = {1000, {.tv_sec = 2148, .tv_nsec = 0}, 2148001, NULL},
+ [111] = {1000, {.tv_sec = 2148, .tv_nsec = 1}, 2148002, NULL},
+ [112] = {1000, {.tv_sec = 2148, .tv_nsec = 2}, 2148002, NULL},
+ /* .tv_sec ~ INT_MAX/hz */
+ [113] = {1000, {.tv_sec = 2147483, .tv_nsec = 643999999}, 2147483645,
+ NULL},
+ [114] = {1000, {.tv_sec = 2147483, .tv_nsec = 644000000}, 2147483645,
+ NULL},
+ [115] = {1000, {.tv_sec = 2147483, .tv_nsec = 644000001}, 2147483646,
+ NULL},
+ [116] = {1000, {.tv_sec = 2147483, .tv_nsec = 644000002}, 2147483646,
+ NULL},
+ [117] = {1000, {.tv_sec = 2147483, .tv_nsec = 644999999}, 2147483646,
+ NULL},
+ [118] = {1000, {.tv_sec = 2147483, .tv_nsec = 645000000}, 2147483646,
+ NULL},
+ /* saturate at INT_MAX = 2^31 - 1 ticks */
+ [119] = {1000, {.tv_sec = 2147483, .tv_nsec = 645000001}, 2147483647,
+ NULL},
+ [120] = {1000, {.tv_sec = 2147483, .tv_nsec = 645000002}, 2147483647,
+ NULL},
+ [121] = {1000, {.tv_sec = 2147483, .tv_nsec = 645999999}, 2147483647,
+ NULL},
+ [122] = {1000, {.tv_sec = 2147483, .tv_nsec = 646000000}, 2147483647,
+ NULL},
+ [123] = {1000, {.tv_sec = 2147483, .tv_nsec = 646000001}, 2147483647,
+ NULL},
+ [124] = {1000, {.tv_sec = 2147483, .tv_nsec = 646000002}, 2147483647,
+ NULL},
+ [125] = {1000, {.tv_sec = 2147483, .tv_nsec = 699999999}, 2147483647,
+ NULL},
+ [126] = {1000, {.tv_sec = 2147484, .tv_nsec = 0}, 2147483647,
+ NULL},
+ [127] = {1000, {.tv_sec = 2147484, .tv_nsec = 1}, 2147483647,
+ NULL},
+ [128] = {1000, {.tv_sec = 2147484, .tv_nsec = 2}, 2147483647,
+ NULL},
+ [129] = {1000, {.tv_sec = 2147484, .tv_nsec = 2}, 2147483647,
+ NULL},
+ /* .tv_sec ~ INT64_MAX/1000000, overflows to INT_MAX ticks */
+ [130] = {1000, {.tv_sec = 9223372036854, .tv_nsec = 999999999},
+ INT_MAX, NULL},
+ [131] = {1000, {.tv_sec = 9223372036855, .tv_nsec = 0},
+ INT_MAX, NULL},
+ [132] = {1000, {.tv_sec = 9223372036855, .tv_nsec = 1},
+ INT_MAX, NULL},
+ [133] = {1000, {.tv_sec = 9223372036855, .tv_nsec = 2},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/hz, overflows to INT_MAX ticks */
+ [134] = {1000, {.tv_sec = 92233720368547758, .tv_nsec = 999999999},
+ INT_MAX, NULL},
+ [135] = {1000, {.tv_sec = 92233720368547758, .tv_nsec = 0},
+ INT_MAX, NULL},
+ [136] = {1000, {.tv_sec = 92233720368547758, .tv_nsec = 1},
+ INT_MAX, NULL},
+ [137] = {1000, {.tv_sec = 92233720368547758, .tv_nsec = 2},
+ INT_MAX, NULL},
+ [138] = {1000, {.tv_sec = (time_t)INT_MAX + 1, .tv_nsec = 123456789},
+ INT_MAX, NULL},
+
+ /*
+ * hz = 8191, prime non-divisor of 10^k or 2^k
+ */
+
+ [139] = {8191, {.tv_sec = -1, .tv_nsec = 0}, 0, NULL},
+ [140] = {8191, {.tv_sec = -1, .tv_nsec = 999999999}, 0, NULL},
+ [141] = {8191, {.tv_sec = 0, .tv_nsec = 0}, 0, NULL},
+ [142] = {8191, {.tv_sec = 0, .tv_nsec = 1}, 2, NULL},
+ [143] = {8191, {.tv_sec = 0, .tv_nsec = 2}, 2, NULL},
+ [144] = {8191, {.tv_sec = 0, .tv_nsec = 122084}, 2,
+ TSTOHZ_ROUND_XFAIL},
+ [145] = {8191, {.tv_sec = 0, .tv_nsec = 122085}, 2,
+ TSTOHZ_ROUND_XFAIL},
+ [146] = {8191, {.tv_sec = 0, .tv_nsec = 122086}, 3, NULL},
+ [147] = {8191, {.tv_sec = 0, .tv_nsec = 244168}, 3,
+ TSTOHZ_ROUND_XFAIL},
+ [148] = {8191, {.tv_sec = 0, .tv_nsec = 244169}, 3,
+ TSTOHZ_ROUND_XFAIL},
+ [149] = {8191, {.tv_sec = 0, .tv_nsec = 244170}, 3,
+ TSTOHZ_ROUND_XFAIL},
+ [150] = {8191, {.tv_sec = 0, .tv_nsec = 244171}, 4, NULL},
+ [151] = {8191, {.tv_sec = 0, .tv_nsec = 244172}, 4, NULL},
+ [152] = {8191, {.tv_sec = 0, .tv_nsec = 999999999}, 8192, NULL},
+ [153] = {8191, {.tv_sec = 1, .tv_nsec = 0}, 8192, NULL},
+ [154] = {8191, {.tv_sec = 1, .tv_nsec = 1}, 8193, NULL},
+ [155] = {8191, {.tv_sec = 1, .tv_nsec = 2}, 8193, NULL},
+ /* .tv_sec ~ INT_MAX/1000000 */
+ [156] = {8191, {.tv_sec = 2147, .tv_nsec = 999999999}, 17594269, NULL},
+ [157] = {8191, {.tv_sec = 2148, .tv_nsec = 0}, 17594269, NULL},
+ [158] = {8191, {.tv_sec = 2148, .tv_nsec = 1}, 17594270, NULL},
+ [159] = {8191, {.tv_sec = 2148, .tv_nsec = 2}, 17594270, NULL},
+ /* .tv_sec ~ INT_MAX/hz */
+ [160] = {8191, {.tv_sec = 262176, .tv_nsec = 3540471}, 2147483646,
+ NULL},
+ [161] = {8191, {.tv_sec = 262176, .tv_nsec = 3540472}, 2147483647,
+ NULL},
+ [162] = {8191, {.tv_sec = 262176, .tv_nsec = 3540473}, 2147483647,
+ NULL},
+ /* saturate at INT_MAX = 2^31 - 1 ticks */
+ [163] = {8191, {.tv_sec = 262176, .tv_nsec = 3662556}, 2147483647,
+ NULL},
+ [164] = {8191, {.tv_sec = 262176, .tv_nsec = 3662557}, 2147483647,
+ NULL},
+ [165] = {8191, {.tv_sec = 262176, .tv_nsec = 3662558}, 2147483647,
+ NULL},
+ [166] = {8191, {.tv_sec = 262176, .tv_nsec = 999999999}, 2147483647,
+ NULL},
+ /* .tv_sec ~ INT64_MAX/1000000, overflows to INT_MAX ticks */
+ [167] = {8191, {.tv_sec = 9223372036854, .tv_nsec = 999999999},
+ INT_MAX, NULL},
+ [168] = {8191, {.tv_sec = 9223372036855, .tv_nsec = 0},
+ INT_MAX, NULL},
+ [169] = {8191, {.tv_sec = 9223372036855, .tv_nsec = 1},
+ INT_MAX, NULL},
+ [170] = {8191, {.tv_sec = 9223372036855, .tv_nsec = 2},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/hz, overflows to INT_MAX ticks */
+ [171] = {8191, {.tv_sec = 92233720368547758, .tv_nsec = 999999999},
+ INT_MAX, NULL},
+ [172] = {8191, {.tv_sec = 92233720368547758, .tv_nsec = 0},
+ INT_MAX, NULL},
+ [173] = {8191, {.tv_sec = 92233720368547758, .tv_nsec = 1},
+ INT_MAX, NULL},
+ [174] = {8191, {.tv_sec = 92233720368547758, .tv_nsec = 2},
+ INT_MAX, NULL},
+ [175] = {8191, {.tv_sec = (time_t)INT_MAX + 1, .tv_nsec = 123456789},
+ INT_MAX, NULL},
+};
+
+ATF_TC(tstohz);
+ATF_TC_HEAD(tstohz, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test tstohz(9)");
+}
+ATF_TC_BODY(tstohz, tc)
+{
+ size_t i;
+
+ for (i = 0; i < __arraycount(tstohz_cases); i++) {
+ const struct tstohz_case *ts = &tstohz_cases[i];
+ int ticks;
+
+ /* set system parameters */
+ hz = ts->ts_hz;
+ tick = 1000000/hz;
+
+ ticks = tstohz(&ts->ts_ts);
+ if (ts->ts_xfail)
+ atf_tc_expect_fail("%s", ts->ts_xfail);
+
+ /*
+ * Allow some slop of one part per thousand in the
+ * arithmetic, but ensure we round up, not down.
+ */
+ ATF_CHECK_MSG(((unsigned)(ticks - ts->ts_ticks) <=
+ (unsigned)ts->ts_ticks/1000),
+ "[%zu] tstohz(%lld.%09ld sec) @ %d Hz:"
+ " expected %d, got %d",
+ i,
+ (long long)ts->ts_ts.tv_sec,
+ (long)ts->ts_ts.tv_nsec,
+ ts->ts_hz,
+ ts->ts_ticks,
+ ticks);
+ if (ts->ts_xfail)
+ atf_tc_expect_pass();
+ }
+}
+
+/*
+ * { 0, if t <= 0;
+ * tvtohz(t sec) @ f Hz = { ceil(t/(1/f)), if that's below INT_MAX;
+ * { INT_MAX, otherwise.
+ */
+
+const struct tvtohz_case {
+ int tv_hz;
+ struct timeval tv_tv;
+ int tv_ticks;
+ const char *tv_xfail;
+} tvtohz_cases[] = {
+ /*
+ * hz = 10
+ */
+
+ /* negative inputs yield 0 ticks */
+ [0] = {10, {.tv_sec = -1, .tv_usec = 0}, 0, NULL},
+ [1] = {10, {.tv_sec = -1, .tv_usec = 999999}, 0, NULL},
+
+ /* zero input yields 0 ticks */
+ [2] = {10, {.tv_sec = 0, .tv_usec = 0}, 0, NULL},
+
+ /*
+ * Nonzero input always yields >=2 ticks, because the time from
+ * now until the next tick may be arbitrarily short, and we
+ * need to wait one full tick, so we have to wait for two
+ * ticks.
+ */
+ [3] = {10, {.tv_sec = 0, .tv_usec = 1}, 2, NULL},
+ [4] = {10, {.tv_sec = 0, .tv_usec = 2}, 2, NULL},
+ [5] = {10, {.tv_sec = 0, .tv_usec = 99999}, 2, NULL},
+ [6] = {10, {.tv_sec = 0, .tv_usec = 100000}, 2, NULL},
+ [7] = {10, {.tv_sec = 0, .tv_usec = 100001}, 3, NULL},
+ [8] = {10, {.tv_sec = 0, .tv_usec = 100002}, 3, NULL},
+ [9] = {10, {.tv_sec = 0, .tv_usec = 199999}, 3, NULL},
+ [10] = {10, {.tv_sec = 0, .tv_usec = 200000}, 3, NULL},
+ [11] = {10, {.tv_sec = 0, .tv_usec = 200001}, 4, NULL},
+ [12] = {10, {.tv_sec = 0, .tv_usec = 200002}, 4, NULL},
+ [13] = {10, {.tv_sec = 0, .tv_usec = 999999}, 11, NULL},
+ [14] = {10, {.tv_sec = 1, .tv_usec = 0}, 11, NULL},
+ [15] = {10, {.tv_sec = 1, .tv_usec = 1}, 12, NULL},
+ [16] = {10, {.tv_sec = 1, .tv_usec = 2}, 12, NULL},
+ /* .tv_sec ~ INT32_MAX/1000000 */
+ [17] = {10, {.tv_sec = 2147, .tv_usec = 999999}, 21481, NULL},
+ [18] = {10, {.tv_sec = 2148, .tv_usec = 0}, 21481, NULL},
+ [19] = {10, {.tv_sec = 2148, .tv_usec = 1}, 21482, NULL},
+ [20] = {10, {.tv_sec = 2148, .tv_usec = 2}, 21482, NULL},
+ /* .tv_sec ~ INT32_MAX/hz */
+ [21] = {10, {.tv_sec = 214748364, .tv_usec = 499999}, 2147483646,
+ NULL},
+ /* saturate at INT_MAX = 2^31 - 1 ticks */
+ [22] = {10, {.tv_sec = 214748364, .tv_usec = 500000}, 2147483646,
+ NULL},
+ [23] = {10, {.tv_sec = 214748364, .tv_usec = 500001}, 2147483647,
+ NULL},
+ [24] = {10, {.tv_sec = 214748364, .tv_usec = 500002}, 2147483647,
+ NULL},
+ [25] = {10, {.tv_sec = 214748364, .tv_usec = 599999}, 2147483647,
+ NULL},
+ [26] = {10, {.tv_sec = 214748364, .tv_usec = 600000}, 2147483647,
+ NULL},
+ [27] = {10, {.tv_sec = 214748364, .tv_usec = 999999}, 2147483647,
+ NULL},
+ [28] = {10, {.tv_sec = 214748365, .tv_usec = 0}, 2147483647,
+ NULL},
+ [29] = {10, {.tv_sec = 214748365, .tv_usec = 1}, 2147483647,
+ NULL},
+ [30] = {10, {.tv_sec = 214748365, .tv_usec = 2}, 2147483647,
+ NULL},
+ [31] = {10, {.tv_sec = (time_t)INT_MAX + 1, .tv_usec = 123456},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/1000000, overflows to INT_MAX ticks */
+ [32] = {10, {.tv_sec = 9223372036854, .tv_usec = 999999},
+ INT_MAX, NULL},
+ [33] = {10, {.tv_sec = 9223372036855, .tv_usec = 0},
+ INT_MAX, NULL},
+ [34] = {10, {.tv_sec = 9223372036855, .tv_usec = 1},
+ INT_MAX, NULL},
+ [35] = {10, {.tv_sec = 9223372036855, .tv_usec = 2},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/hz, overflows to INT_MAX ticks */
+ [36] = {10, {.tv_sec = 922337203685477580, .tv_usec = 999999},
+ INT_MAX, NULL},
+ [37] = {10, {.tv_sec = 922337203685477581, .tv_usec = 0},
+ INT_MAX, NULL},
+ [38] = {10, {.tv_sec = 922337203685477581, .tv_usec = 1},
+ INT_MAX, NULL},
+ [39] = {10, {.tv_sec = 922337203685477581, .tv_usec = 2},
+ INT_MAX, NULL},
+ [40] = {10, {.tv_sec = (time_t)INT_MAX + 1, .tv_usec = 123456},
+ INT_MAX, NULL},
+
+ /*
+ * hz = 100
+ */
+
+ [41] = {100, {.tv_sec = -1, .tv_usec = 0}, 0, NULL},
+ [42] = {100, {.tv_sec = -1, .tv_usec = 999999}, 0, NULL},
+ [43] = {100, {.tv_sec = 0, .tv_usec = 0}, 0, NULL},
+ [44] = {100, {.tv_sec = 0, .tv_usec = 1}, 2, NULL},
+ [45] = {100, {.tv_sec = 0, .tv_usec = 2}, 2, NULL},
+ [46] = {100, {.tv_sec = 0, .tv_usec = 9999}, 2, NULL},
+ [47] = {100, {.tv_sec = 0, .tv_usec = 10000}, 2, NULL},
+ [48] = {100, {.tv_sec = 0, .tv_usec = 10001}, 3, NULL},
+ [49] = {100, {.tv_sec = 0, .tv_usec = 10002}, 3, NULL},
+ [50] = {100, {.tv_sec = 0, .tv_usec = 19999}, 3, NULL},
+ [51] = {100, {.tv_sec = 0, .tv_usec = 20000}, 3, NULL},
+ [52] = {100, {.tv_sec = 0, .tv_usec = 20001}, 4, NULL},
+ [53] = {100, {.tv_sec = 0, .tv_usec = 20002}, 4, NULL},
+ [54] = {100, {.tv_sec = 0, .tv_usec = 99999}, 11, NULL},
+ [55] = {100, {.tv_sec = 0, .tv_usec = 100000}, 11, NULL},
+ [56] = {100, {.tv_sec = 0, .tv_usec = 100001}, 12, NULL},
+ [57] = {100, {.tv_sec = 0, .tv_usec = 100002}, 12, NULL},
+ [58] = {100, {.tv_sec = 0, .tv_usec = 999999}, 101, NULL},
+ [59] = {100, {.tv_sec = 1, .tv_usec = 0}, 101, NULL},
+ [60] = {100, {.tv_sec = 1, .tv_usec = 1}, 102, NULL},
+ [61] = {100, {.tv_sec = 1, .tv_usec = 2}, 102, NULL},
+ /* .tv_sec ~ INT32_MAX/1000000 */
+ [62] = {100, {.tv_sec = 2147, .tv_usec = 999999}, 214801, NULL},
+ [63] = {100, {.tv_sec = 2148, .tv_usec = 0}, 214801, NULL},
+ [64] = {100, {.tv_sec = 2148, .tv_usec = 1}, 214802, NULL},
+ [65] = {100, {.tv_sec = 2148, .tv_usec = 2}, 214802, NULL},
+ /* .tv_sec ~ INT32_MAX/hz */
+ [66] = {100, {.tv_sec = 21474836, .tv_usec = 439999}, 2147483645,
+ NULL},
+ [67] = {100, {.tv_sec = 21474836, .tv_usec = 440000}, 2147483645,
+ NULL},
+ [68] = {100, {.tv_sec = 21474836, .tv_usec = 440001}, 2147483646,
+ NULL},
+ [69] = {100, {.tv_sec = 21474836, .tv_usec = 440002}, 2147483646,
+ NULL},
+ [70] = {100, {.tv_sec = 21474836, .tv_usec = 449999}, 2147483646,
+ NULL},
+ [71] = {100, {.tv_sec = 21474836, .tv_usec = 450000}, 2147483646,
+ NULL},
+ /* saturate at INT_MAX = 2^31 - 1 ticks */
+ [72] = {100, {.tv_sec = 21474836, .tv_usec = 450001}, 2147483647,
+ NULL},
+ [73] = {100, {.tv_sec = 21474836, .tv_usec = 450002}, 2147483647,
+ NULL},
+ [74] = {100, {.tv_sec = 21474836, .tv_usec = 459999}, 2147483647,
+ NULL},
+ [75] = {100, {.tv_sec = 21474836, .tv_usec = 460000}, 2147483647,
+ NULL},
+ [76] = {100, {.tv_sec = 21474836, .tv_usec = 460001}, 2147483647,
+ NULL},
+ [77] = {100, {.tv_sec = 21474836, .tv_usec = 460002}, 2147483647,
+ NULL},
+ [78] = {100, {.tv_sec = 21474836, .tv_usec = 999999}, 2147483647,
+ NULL},
+ [79] = {100, {.tv_sec = 21474837, .tv_usec = 0}, 2147483647,
+ NULL},
+ [80] = {100, {.tv_sec = 21474837, .tv_usec = 1}, 2147483647,
+ NULL},
+ [81] = {100, {.tv_sec = 21474837, .tv_usec = 2}, 2147483647,
+ NULL},
+ [82] = {100, {.tv_sec = 21474837, .tv_usec = 2}, 2147483647,
+ NULL},
+ /* .tv_sec ~ INT64_MAX/1000000 */
+ [83] = {100, {.tv_sec = 9223372036854, .tv_usec = 999999},
+ INT_MAX, NULL},
+ [84] = {100, {.tv_sec = 9223372036855, .tv_usec = 0},
+ INT_MAX, NULL},
+ [85] = {100, {.tv_sec = 9223372036855, .tv_usec = 1},
+ INT_MAX, NULL},
+ [86] = {100, {.tv_sec = 9223372036855, .tv_usec = 2},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/hz, overflows to INT_MAX ticks */
+ [87] = {100, {.tv_sec = 92233720368547758, .tv_usec = 999999},
+ INT_MAX, NULL},
+ [88] = {100, {.tv_sec = 92233720368547758, .tv_usec = 0},
+ INT_MAX, NULL},
+ [89] = {100, {.tv_sec = 92233720368547758, .tv_usec = 1},
+ INT_MAX, NULL},
+ [90] = {100, {.tv_sec = 92233720368547758, .tv_usec = 2},
+ INT_MAX, NULL},
+ [91] = {100, {.tv_sec = (time_t)INT_MAX + 1, .tv_usec = 123456},
+ INT_MAX, NULL},
+
+ /*
+ * hz = 1000
+ */
+
+ [92] = {1000, {.tv_sec = -1, .tv_usec = 0}, 0, NULL},
+ [93] = {1000, {.tv_sec = -1, .tv_usec = 999999}, 0, NULL},
+ [94] = {1000, {.tv_sec = 0, .tv_usec = 0}, 0, NULL},
+ [95] = {1000, {.tv_sec = 0, .tv_usec = 1}, 2, NULL},
+ [96] = {1000, {.tv_sec = 0, .tv_usec = 2}, 2, NULL},
+ [97] = {1000, {.tv_sec = 0, .tv_usec = 999}, 2, NULL},
+ [98] = {1000, {.tv_sec = 0, .tv_usec = 1000}, 2, NULL},
+ [99] = {1000, {.tv_sec = 0, .tv_usec = 1001}, 3, NULL},
+ [100] = {1000, {.tv_sec = 0, .tv_usec = 1002}, 3, NULL},
+ [101] = {1000, {.tv_sec = 0, .tv_usec = 1999}, 3, NULL},
+ [102] = {1000, {.tv_sec = 0, .tv_usec = 2000}, 3, NULL},
+ [103] = {1000, {.tv_sec = 0, .tv_usec = 2001}, 4, NULL},
+ [104] = {1000, {.tv_sec = 0, .tv_usec = 2002}, 4, NULL},
+ [105] = {1000, {.tv_sec = 0, .tv_usec = 999999}, 1001, NULL},
+ [106] = {1000, {.tv_sec = 1, .tv_usec = 0}, 1001, NULL},
+ [107] = {1000, {.tv_sec = 1, .tv_usec = 1}, 1002, NULL},
+ [108] = {1000, {.tv_sec = 1, .tv_usec = 2}, 1002, NULL},
+ /* .tv_sec ~ INT_MAX/1000000 */
+ [109] = {1000, {.tv_sec = 2147, .tv_usec = 999999}, 2148001, NULL},
+ [110] = {1000, {.tv_sec = 2148, .tv_usec = 0}, 2148001, NULL},
+ [111] = {1000, {.tv_sec = 2148, .tv_usec = 1}, 2148002, NULL},
+ [112] = {1000, {.tv_sec = 2148, .tv_usec = 2}, 2148002, NULL},
+ /* .tv_sec ~ INT_MAX/hz */
+ [113] = {1000, {.tv_sec = 2147483, .tv_usec = 643999}, 2147483645,
+ NULL},
+ [114] = {1000, {.tv_sec = 2147483, .tv_usec = 644000}, 2147483645,
+ NULL},
+ [115] = {1000, {.tv_sec = 2147483, .tv_usec = 644001}, 2147483646,
+ NULL},
+ [116] = {1000, {.tv_sec = 2147483, .tv_usec = 644002}, 2147483646,
+ NULL},
+ [117] = {1000, {.tv_sec = 2147483, .tv_usec = 644999}, 2147483646,
+ NULL},
+ [118] = {1000, {.tv_sec = 2147483, .tv_usec = 645000}, 2147483646,
+ NULL},
+ /* saturate at INT_MAX = 2^31 - 1 ticks */
+ [119] = {1000, {.tv_sec = 2147483, .tv_usec = 645001}, 2147483647,
+ NULL},
+ [120] = {1000, {.tv_sec = 2147483, .tv_usec = 645002}, 2147483647,
+ NULL},
+ [121] = {1000, {.tv_sec = 2147483, .tv_usec = 645999}, 2147483647,
+ NULL},
+ [122] = {1000, {.tv_sec = 2147483, .tv_usec = 646000}, 2147483647,
+ NULL},
+ [123] = {1000, {.tv_sec = 2147483, .tv_usec = 646001}, 2147483647,
+ NULL},
+ [124] = {1000, {.tv_sec = 2147483, .tv_usec = 646002}, 2147483647,
+ NULL},
+ [125] = {1000, {.tv_sec = 2147483, .tv_usec = 699999}, 2147483647,
+ NULL},
+ [126] = {1000, {.tv_sec = 2147484, .tv_usec = 0}, 2147483647,
+ NULL},
+ [127] = {1000, {.tv_sec = 2147484, .tv_usec = 1}, 2147483647,
+ NULL},
+ [128] = {1000, {.tv_sec = 2147484, .tv_usec = 2}, 2147483647,
+ NULL},
+ [129] = {1000, {.tv_sec = 2147484, .tv_usec = 2}, 2147483647,
+ NULL},
+ /* .tv_sec ~ INT64_MAX/1000000, overflows to INT_MAX ticks */
+ [130] = {1000, {.tv_sec = 9223372036854, .tv_usec = 999999},
+ INT_MAX, NULL},
+ [131] = {1000, {.tv_sec = 9223372036855, .tv_usec = 0},
+ INT_MAX, NULL},
+ [132] = {1000, {.tv_sec = 9223372036855, .tv_usec = 1},
+ INT_MAX, NULL},
+ [133] = {1000, {.tv_sec = 9223372036855, .tv_usec = 2},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/hz, overflows to INT_MAX ticks */
+ [134] = {1000, {.tv_sec = 92233720368547758, .tv_usec = 999999},
+ INT_MAX, NULL},
+ [135] = {1000, {.tv_sec = 92233720368547758, .tv_usec = 0},
+ INT_MAX, NULL},
+ [136] = {1000, {.tv_sec = 92233720368547758, .tv_usec = 1},
+ INT_MAX, NULL},
+ [137] = {1000, {.tv_sec = 92233720368547758, .tv_usec = 2},
+ INT_MAX, NULL},
+ [138] = {1000, {.tv_sec = (time_t)INT_MAX + 1, .tv_usec = 123456},
+ INT_MAX, NULL},
+
+ /*
+ * hz = 8191, prime non-divisor of 10^k or 2^k
+ */
+
+ [139] = {8191, {.tv_sec = -1, .tv_usec = 0}, 0, NULL},
+ [140] = {8191, {.tv_sec = -1, .tv_usec = 999999}, 0, NULL},
+ [141] = {8191, {.tv_sec = 0, .tv_usec = 0}, 0, NULL},
+ [142] = {8191, {.tv_sec = 0, .tv_usec = 1}, 2, NULL},
+ [143] = {8191, {.tv_sec = 0, .tv_usec = 2}, 2, NULL},
+ [144] = {8191, {.tv_sec = 0, .tv_usec = 121}, 2, NULL},
+ [145] = {8191, {.tv_sec = 0, .tv_usec = 122}, 2, NULL},
+ [146] = {8191, {.tv_sec = 0, .tv_usec = 123}, 3, NULL},
+ [147] = {8191, {.tv_sec = 0, .tv_usec = 242}, 3, NULL},
+ [148] = {8191, {.tv_sec = 0, .tv_usec = 243}, 3, NULL},
+ [149] = {8191, {.tv_sec = 0, .tv_usec = 244}, 3, NULL},
+ [150] = {8191, {.tv_sec = 0, .tv_usec = 245}, 4, NULL},
+ [151] = {8191, {.tv_sec = 0, .tv_usec = 246}, 4, NULL},
+ [152] = {8191, {.tv_sec = 0, .tv_usec = 999999}, 8192, NULL},
+ [153] = {8191, {.tv_sec = 1, .tv_usec = 0}, 8192, NULL},
+ [154] = {8191, {.tv_sec = 1, .tv_usec = 1}, 8193, NULL},
+ [155] = {8191, {.tv_sec = 1, .tv_usec = 2}, 8193, NULL},
+ /* .tv_sec ~ INT_MAX/1000000 */
+ [156] = {8191, {.tv_sec = 2147, .tv_usec = 999999}, 17594269, NULL},
+ [157] = {8191, {.tv_sec = 2148, .tv_usec = 0}, 17594269, NULL},
+ [158] = {8191, {.tv_sec = 2148, .tv_usec = 1}, 17594270, NULL},
+ [159] = {8191, {.tv_sec = 2148, .tv_usec = 2}, 17594270, NULL},
+ /* .tv_sec ~ INT_MAX/hz */
+ [160] = {8191, {.tv_sec = 262176, .tv_usec = 3540}, 2147483646,
+ NULL},
+ [161] = {8191, {.tv_sec = 262176, .tv_usec = 3541}, 2147483647,
+ NULL},
+ [162] = {8191, {.tv_sec = 262176, .tv_usec = 3542}, 2147483647,
+ NULL},
+ /* saturate at INT_MAX = 2^31 - 1 ticks */
+ [163] = {8191, {.tv_sec = 262176, .tv_usec = 3662}, 2147483647,
+ NULL},
+ [164] = {8191, {.tv_sec = 262176, .tv_usec = 3663}, 2147483647,
+ NULL},
+ [165] = {8191, {.tv_sec = 262176, .tv_usec = 3664}, 2147483647,
+ NULL},
+ [166] = {8191, {.tv_sec = 262176, .tv_usec = 999999}, 2147483647,
+ NULL},
+ /* .tv_sec ~ INT64_MAX/1000000, overflows to INT_MAX ticks */
+ [167] = {8191, {.tv_sec = 9223372036854, .tv_usec = 999999},
+ INT_MAX, NULL},
+ [168] = {8191, {.tv_sec = 9223372036855, .tv_usec = 0},
+ INT_MAX, NULL},
+ [169] = {8191, {.tv_sec = 9223372036855, .tv_usec = 1},
+ INT_MAX, NULL},
+ [170] = {8191, {.tv_sec = 9223372036855, .tv_usec = 2},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/hz, overflows to INT_MAX ticks */
+ [171] = {8191, {.tv_sec = 92233720368547758, .tv_usec = 999999},
+ INT_MAX, NULL},
+ [172] = {8191, {.tv_sec = 92233720368547758, .tv_usec = 0},
+ INT_MAX, NULL},
+ [173] = {8191, {.tv_sec = 92233720368547758, .tv_usec = 1},
+ INT_MAX, NULL},
+ [174] = {8191, {.tv_sec = 92233720368547758, .tv_usec = 2},
+ INT_MAX, NULL},
+ [175] = {8191, {.tv_sec = (time_t)INT_MAX + 1, .tv_usec = 123456},
+ INT_MAX, NULL},
+};
+
+ATF_TC(tvtohz);
+ATF_TC_HEAD(tvtohz, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test tvtohz(9)");
+}
+ATF_TC_BODY(tvtohz, tc)
+{
+ size_t i;
+
+ for (i = 0; i < __arraycount(tvtohz_cases); i++) {
+ const struct tvtohz_case *tv = &tvtohz_cases[i];
+ int ticks;
+
+ /* set system parameters */
+ hz = tv->tv_hz;
+ tick = 1000000/hz;
+
+ ticks = tvtohz(&tv->tv_tv);
+ if (tv->tv_xfail)
+ atf_tc_expect_fail("%s", tv->tv_xfail);
+
+ /*
+ * Allow some slop of one part per thousand in the
+ * arithmetic, but ensure we round up, not down.
+ *
+ * XXX Analytically determine error bounds on the
+ * formulae we use and assess them.
+ */
+ ATF_CHECK_MSG(((unsigned)(ticks - tv->tv_ticks) <=
+ (unsigned)tv->tv_ticks/1000),
+ "[%zu] tvtohz(%lld.%06ld sec) @ %d Hz:"
+ " expected %d, got %d",
+ i,
+ (long long)tv->tv_tv.tv_sec,
+ (long)tv->tv_tv.tv_usec,
+ tv->tv_hz,
+ tv->tv_ticks,
+ ticks);
+ if (tv->tv_xfail)
+ atf_tc_expect_pass();
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, itimer_transitions);
+ ATF_TP_ADD_TC(tp, tstohz);
+ ATF_TP_ADD_TC(tp, tvtohz);
+
+ return atf_no_error();
+}
+
diff --git a/kernel/t_unmount.c b/kernel/t_unmount.c
new file mode 100644
index 000000000000..41ed39082e61
--- /dev/null
+++ b/kernel/t_unmount.c
@@ -0,0 +1,106 @@
+/* $NetBSD: t_unmount.c,v 1.4 2024/10/02 17:16:32 bad Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <sys/cdefs.h>
+__COPYRIGHT("@(#) Copyright (c) 2024\
+ The NetBSD Foundation, inc. All rights reserved.");
+__RCSID("$NetBSD: t_unmount.c,v 1.4 2024/10/02 17:16:32 bad Exp $");
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+
+#include <rump/rump.h>
+#include <rump/rumpvnode_if.h>
+#include <rump/rump_syscalls.h>
+
+#include <fs/tmpfs/tmpfs_args.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "h_macros.h"
+
+ATF_TC(async);
+ATF_TC_HEAD(async, tc)
+{
+ atf_tc_set_md_var(tc,
+ "descr", "failed unmount of async fs should stay async");
+}
+
+#define MP "/mnt"
+
+ATF_TC_BODY(async, tc)
+{
+ struct tmpfs_args args;
+ struct vnode *vp;
+ extern struct vnode *rumpns_rootvnode;
+
+ RZ(rump_init());
+
+ memset(&args, 0, sizeof(args));
+ args.ta_version = TMPFS_ARGS_VERSION;
+ args.ta_root_mode = 0777;
+
+ /* create mount point and mount a tmpfs on it */
+ RL(rump_sys_mkdir(MP, 0777));
+ RL(rump_sys_mount(MOUNT_TMPFS, MP, MNT_ASYNC, &args, sizeof(args)));
+
+ /* make sure the tmpfs is busy */
+ RL(rump_sys_chdir(MP));
+
+ /* need a stable lwp for componentname */
+ RZ(rump_pub_lwproc_rfork(RUMP_RFCFDG));
+
+ /* get vnode of MP, unlocked */
+ RZ(rump_pub_namei(RUMP_NAMEI_LOOKUP, 0, MP, NULL, &vp, NULL));
+
+ /* make sure we didn't just get the root vnode */
+ ATF_REQUIRE_MSG((rumpns_rootvnode != vp), "drat! got the root vnode");
+
+ printf("mnt_iflag & IMNT_ONWORKLIST == %d\n",
+ (vp->v_mount->mnt_iflag & IMNT_ONWORKLIST) != 0);
+
+ /* can't unmount a busy file system */
+ ATF_REQUIRE_ERRNO(EBUSY, rump_sys_unmount(MP, 0) == -1);
+
+ printf("mnt_iflag & IMNT_ONWORKLIST == %d\n",
+ (vp->v_mount->mnt_iflag & IMNT_ONWORKLIST) != 0);
+
+
+ ATF_REQUIRE_MSG(((vp->v_mount->mnt_iflag & IMNT_ONWORKLIST) == 0),
+ "mount point on syncer work list");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, async);
+
+ return atf_no_error();
+}
diff --git a/lib/csu/h_hello.c b/lib/csu/h_hello.c
new file mode 100644
index 000000000000..e39284c2f5c9
--- /dev/null
+++ b/lib/csu/h_hello.c
@@ -0,0 +1,48 @@
+/* $NetBSD: h_hello.c,v 1.1 2025/04/27 04:09:35 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: h_hello.c,v 1.1 2025/04/27 04:09:35 riastradh Exp $");
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/*
+ * Force some R_*_RELATIVE-type relocations.
+ */
+static int foo = 42;
+static int *volatile foop = &foo;
+
+int
+main(void)
+{
+
+ printf("%s: Hello, world! %d\n", getprogname(), *foop);
+ fflush(stdout);
+ return ferror(stdout);
+}
diff --git a/lib/csu/h_preinit_array.c b/lib/csu/h_preinit_array.c
new file mode 100644
index 000000000000..36fa37ff340b
--- /dev/null
+++ b/lib/csu/h_preinit_array.c
@@ -0,0 +1,16 @@
+static int x = 1;
+
+static void
+foo(void)
+{
+ x--;
+}
+
+static void (*fp) (void) __attribute__((__section__(".preinit_array"), __used__)) =
+ foo;
+
+int
+main(void)
+{
+ return x;
+}
diff --git a/lib/csu/t_hello.sh b/lib/csu/t_hello.sh
new file mode 100644
index 000000000000..b72f62885476
--- /dev/null
+++ b/lib/csu/t_hello.sh
@@ -0,0 +1,150 @@
+# $NetBSD: t_hello.sh,v 1.3 2025/05/02 23:04:06 riastradh Exp $
+#
+# Copyright (c) 2025 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+checksupport()
+{
+ local prog
+
+ prog=$1
+ test -f "$(atf_get_srcdir)/${prog}" || atf_skip "not supported"
+}
+
+checkrun()
+{
+ local prog
+
+ prog=$1
+ atf_check -o inline:"${prog}: Hello, world! 42\n" \
+ "$(atf_get_srcdir)/${prog}"
+}
+
+cleanup()
+{
+ local prog
+
+ prog=$1
+ test -f "${prog}.core" || return 0
+ readelf -rs "$(atf_get_srcdir)/${prog}"
+ gdb -batch -ex bt -ex 'info registers' -ex disas \
+ "$(atf_get_srcdir)/${prog}" "${prog}.core"
+}
+
+atf_test_case dynamic cleanup
+dynamic_head()
+{
+ atf_set "descr" "Test a dynamic executable"
+}
+dynamic_body()
+{
+ checksupport h_hello_dyn
+ checkrun h_hello_dyn
+}
+dynamic_cleanup()
+{
+ cleanup h_hello_dyn
+}
+
+atf_test_case dynamicpie cleanup
+dynamicpie_head()
+{
+ atf_set "descr" "Test a dynamic position-independent executable"
+}
+dynamicpie_body()
+{
+ checksupport h_hello_dynpie
+ checkrun h_hello_dynpie
+}
+dynamicpie_cleanup()
+{
+ cleanup h_hello_dynpie
+}
+
+atf_test_case relr cleanup
+relr_head()
+{
+ atf_set "descr" "Test a static PIE executable with RELR relocations"
+}
+relr_body()
+{
+ checksupport h_hello_relr
+ case `uname -p` in
+ i386|x86_64)
+ ;;
+ *) atf_expect_fail "PR lib/59359: static pies are broken"
+ ;;
+ esac
+ checkrun h_hello_relr
+}
+relr_cleanup()
+{
+ cleanup h_hello_relr
+}
+
+atf_test_case static cleanup
+static_head()
+{
+ atf_set "descr" "Test a static executable"
+}
+static_body()
+{
+ checksupport h_hello_sta
+ checkrun h_hello_sta
+}
+static_cleanup()
+{
+ cleanup h_hello_sta
+}
+
+atf_test_case staticpie cleanup
+staticpie_head()
+{
+ atf_set "descr" "Test a static position-independent executable"
+}
+staticpie_body()
+{
+ checksupport h_hello_stapie
+ case `uname -p` in
+ i386|x86_64)
+ ;;
+ *) atf_expect_fail "PR lib/59359: static pies are broken"
+ ;;
+ esac
+ checkrun h_hello_stapie
+}
+staticpie_cleanup()
+{
+ cleanup h_hello_stapie
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case dynamic
+ atf_add_test_case dynamicpie
+ atf_add_test_case relr
+ atf_add_test_case static
+ atf_add_test_case staticpie
+}
diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc
new file mode 100644
index 000000000000..01b5f23410c8
--- /dev/null
+++ b/lib/libc/gen/Makefile.inc
@@ -0,0 +1 @@
+.include "../Makefile.inc"
diff --git a/lib/libc/gen/h_ctype_abuse.c b/lib/libc/gen/h_ctype_abuse.c
new file mode 100644
index 000000000000..fdff8552f8f4
--- /dev/null
+++ b/lib/libc/gen/h_ctype_abuse.c
@@ -0,0 +1,138 @@
+/* $NetBSD: h_ctype_abuse.c,v 1.2 2025/09/15 17:32:01 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Helper program to verify effects of ctype(3) abuse.
+ *
+ * NOTE: This program intentionally triggers undefined behaviour by
+ * passing int values to the ctype(3) functions which are neither EOF
+ * nor representable by unsigned char. The purpose is to verify that
+ * NetBSD's ctype(3) _does not_ trap the undefined behaviour when the
+ * environment variable LIBC_ALLOWCTYPEABUSE. (It does not check
+ * anything about the results, which are perforce nonsense -- just that
+ * it gets a result without crashing.)
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: h_ctype_abuse.c,v 1.2 2025/09/15 17:32:01 riastradh Exp $");
+
+#include <ctype.h>
+#include <err.h>
+#include <limits.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define FOREACHCTYPE(M) \
+ M(ISALPHA, isalpha) \
+ M(ISUPPER, isupper) \
+ M(ISLOWER, islower) \
+ M(ISDIGIT, isdigit) \
+ M(ISXDIGIT, isxdigit) \
+ M(ISALNUM, isalnum) \
+ M(ISSPACE, isspace) \
+ M(ISPUNCT, ispunct) \
+ M(ISPRINT, isprint) \
+ M(ISGRAPH, isgraph) \
+ M(ISCNTRL, iscntrl) \
+ M(ISBLANK, isblank) \
+ M(TOUPPER, toupper) \
+ M(TOLOWER, tolower)
+
+int
+main(int argc, char **argv)
+{
+ enum {
+#define M(upper, lower) upper,
+ FOREACHCTYPE(M)
+#undef M
+ } fn;
+ enum {
+ MACRO,
+ FUNCTION,
+ } mode;
+ int ch;
+ volatile int result;
+
+ setprogname(argv[0]);
+ if (argc != 3 && argc != 4) {
+ errx(1, "Usage: %s <function> <mode> [<locale>]",
+ getprogname());
+ }
+
+#define M(upper, lower) \
+ else if (strcmp(argv[1], #lower) == 0) \
+ fn = upper;
+
+ if (0)
+ ;
+ FOREACHCTYPE(M)
+ else
+ errx(1, "unknown ctype function");
+#undef M
+
+ if (strcmp(argv[2], "macro") == 0)
+ mode = MACRO;
+ else if (strcmp(argv[2], "function") == 0)
+ mode = FUNCTION;
+ else
+ errx(1, "unknown usage mode");
+
+ if (argc == 4) {
+ if (setlocale(LC_CTYPE, argv[3]) == NULL)
+ err(1, "setlocale");
+ }
+
+ /*
+ * Make sure we cover EOF as well.
+ */
+ __CTASSERT(CHAR_MIN == 0 || CHAR_MIN <= EOF);
+ __CTASSERT(EOF <= UCHAR_MAX);
+
+ for (ch = (CHAR_MIN == 0 ? EOF : CHAR_MIN); ch <= UCHAR_MAX; ch++) {
+ switch (fn) {
+#define M(upper, lower) \
+ case upper: \
+ switch (mode) { \
+ case MACRO: \
+ result = lower(ch); \
+ break; \
+ case FUNCTION: \
+ result = (lower)(ch); \
+ break; \
+ } \
+ break;
+ FOREACHCTYPE(M)
+#undef M
+ }
+ (void)result;
+ }
+
+ return 0;
+}
diff --git a/lib/libc/gen/h_execsig.c b/lib/libc/gen/h_execsig.c
new file mode 100644
index 000000000000..a954ca071094
--- /dev/null
+++ b/lib/libc/gen/h_execsig.c
@@ -0,0 +1,55 @@
+/* $NetBSD: h_execsig.c,v 1.1 2025/03/13 01:27:27 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: h_execsig.c,v 1.1 2025/03/13 01:27:27 riastradh Exp $");
+
+/*
+ * Helper program for testing signal delivery during execve(2) and
+ * posix_spawn(2). The caller will:
+ *
+ * 1. fork and exec, or spawn
+ * 2. kill the child
+ * 3. write a byte to be read from the child's stdin
+ *
+ * Since the caller issues syscalls in that order, the signal should be
+ * delivered to the child first, and it should be interrupted by a
+ * signal before it returnsa byte from read(2).
+ */
+
+#include <err.h>
+#include <unistd.h>
+
+int
+main(void)
+{
+
+ if (read(STDIN_FILENO, (char[]){0}, 1) == -1)
+ err(1, "read");
+ return 0;
+}
diff --git a/lib/libc/gen/t_arc4random.c b/lib/libc/gen/t_arc4random.c
new file mode 100644
index 000000000000..b26c1b8a931a
--- /dev/null
+++ b/lib/libc/gen/t_arc4random.c
@@ -0,0 +1,670 @@
+/* $NetBSD: t_arc4random.c,v 1.5 2025/03/09 18:11:55 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _REENTRANT
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_arc4random.c,v 1.5 2025/03/09 18:11:55 riastradh Exp $");
+
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "arc4random.h"
+#include "reentrant.h"
+#include "h_macros.h"
+
+/*
+ * iszero(buf, len)
+ *
+ * True if len bytes at buf are all zero, false if any one of them
+ * is nonzero.
+ */
+static bool
+iszero(const void *buf, size_t len)
+{
+ const unsigned char *p = buf;
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (p[i] != 0)
+ return false;
+ }
+ return true;
+}
+
+/*
+ * arc4random_prng()
+ *
+ * Get a pointer to the current arc4random state, without updating
+ * any of the state, not even lazy initialization.
+ */
+static struct arc4random_prng *
+arc4random_prng(void)
+{
+ struct arc4random_prng *prng = NULL;
+
+ /*
+ * If arc4random has been initialized and there is a thread key
+ * (i.e., libc was built with _REENTRANT), get the thread-local
+ * arc4random state if there is one.
+ */
+ if (arc4random_global.per_thread)
+ prng = thr_getspecific(arc4random_global.thread_key);
+
+ /*
+ * If we couldn't get the thread-local state, get the global
+ * state instead.
+ */
+ if (prng == NULL)
+ prng = &arc4random_global.prng;
+
+ return prng;
+}
+
+/*
+ * arc4random_global_buf(buf, len)
+ *
+ * Same as arc4random_buf, but force use of the global state.
+ * Must happen before any other use of arc4random.
+ */
+static void
+arc4random_global_buf(void *buf, size_t len)
+{
+ struct rlimit rlim, orlim;
+ struct arc4random_prng *prng;
+
+ /*
+ * Save the address space limit.
+ */
+ RL(getrlimit(RLIMIT_AS, &orlim));
+ memcpy(&rlim, &orlim, sizeof(rlim));
+
+ /*
+ * Get a sample while the address space limit is zero. This
+ * should try, and fail, to allocate a thread-local arc4random
+ * state with mmap(2).
+ */
+ rlim.rlim_cur = 0;
+ RL(setrlimit(RLIMIT_AS, &rlim));
+ arc4random_buf(buf, len);
+ RL(setrlimit(RLIMIT_AS, &orlim));
+
+ /*
+ * Restore the address space limit.
+ */
+ RL(setrlimit(RLIMIT_AS, &orlim));
+
+ /*
+ * Verify the PRNG is the global one, not the thread-local one,
+ * and that it was initialized.
+ */
+ prng = arc4random_prng();
+ ATF_CHECK_EQ(prng, &arc4random_global.prng);
+ ATF_CHECK(!iszero(&prng->arc4_prng, sizeof(prng->arc4_prng)));
+ ATF_CHECK(prng->arc4_epoch != 0);
+}
+
+/*
+ * arc4random_global_thread(cookie)
+ *
+ * Start routine for a thread that just grabs an output from the
+ * global state.
+ */
+static void *
+arc4random_global_thread(void *cookie)
+{
+ unsigned char buf[32];
+
+ arc4random_global_buf(buf, sizeof(buf));
+
+ return NULL;
+}
+
+ATF_TC(addrandom);
+ATF_TC_HEAD(addrandom, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test arc4random_addrandom updates the state");
+}
+ATF_TC_BODY(addrandom, tc)
+{
+ unsigned char buf[32], zero32[32] = {0};
+ struct arc4random_prng *prng, copy;
+
+ /*
+ * Get a sample to start things off.
+ */
+ arc4random_buf(buf, sizeof(buf));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+
+ /*
+ * By this point, the global state must be initialized -- if
+ * not, the process should have aborted.
+ */
+ ATF_CHECK(arc4random_global.initialized);
+
+ /*
+ * Get the PRNG, global or local. By this point, the PRNG
+ * state should be nonzero (with overwhelmingly high
+ * probability) and the epoch should also be nonzero.
+ */
+ prng = arc4random_prng();
+ ATF_CHECK(!iszero(&prng->arc4_prng, sizeof(prng->arc4_prng)));
+ ATF_CHECK(prng->arc4_epoch != 0);
+
+ /*
+ * Save a copy and update the state with arc4random_addrandom.
+ */
+ copy = *prng;
+ arc4random_addrandom(zero32, sizeof(zero32));
+
+ /*
+ * The state should have changed. (The epoch may or may not.)
+ */
+ ATF_CHECK(memcmp(&prng->arc4_prng, &copy.arc4_prng,
+ sizeof(copy.arc4_prng)) != 0);
+
+ /*
+ * Save a copy and update the state with arc4random_stir.
+ */
+ copy = *prng;
+ arc4random_stir();
+
+ /*
+ * The state should have changed. (The epoch may or may not.)
+ */
+ ATF_CHECK(memcmp(&prng->arc4_prng, &copy.arc4_prng,
+ sizeof(copy.arc4_prng)) != 0);
+}
+
+ATF_TC(consolidate);
+ATF_TC_HEAD(consolidate, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test consolidating entropy resets the epoch");
+}
+ATF_TC_BODY(consolidate, tc)
+{
+ unsigned char buf[32];
+ struct arc4random_prng *local, *global = &arc4random_global.prng;
+ unsigned localepoch, globalepoch;
+ const int consolidate = 1;
+ pthread_t thread;
+
+ /*
+ * Get a sample from the global state to make sure the global
+ * state is initialized. Remember the epoch.
+ */
+ arc4random_global_buf(buf, sizeof(buf));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+ ATF_CHECK(!iszero(&global->arc4_prng, sizeof(global->arc4_prng)));
+ ATF_CHECK((globalepoch = global->arc4_epoch) != 0);
+
+ /*
+ * Get a sample from the local state too to make sure the local
+ * state is initialized. Remember the epoch.
+ */
+ arc4random_buf(buf, sizeof(buf));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+ local = arc4random_prng();
+ ATF_CHECK(!iszero(&local->arc4_prng, sizeof(local->arc4_prng)));
+ ATF_CHECK((localepoch = local->arc4_epoch) != 0);
+
+ /*
+ * Trigger entropy consolidation.
+ */
+ RL(sysctlbyname("kern.entropy.consolidate", /*oldp*/NULL, /*oldlen*/0,
+ &consolidate, sizeof(consolidate)));
+
+ /*
+ * Verify the epoch cache isn't changed yet until we ask for
+ * more data.
+ */
+ ATF_CHECK_EQ_MSG(globalepoch, global->arc4_epoch,
+ "global epoch was %u, now %u", globalepoch, global->arc4_epoch);
+ ATF_CHECK_EQ_MSG(localepoch, local->arc4_epoch,
+ "local epoch was %u, now %u", localepoch, local->arc4_epoch);
+
+ /*
+ * Request new output and verify the local epoch cache has
+ * changed.
+ */
+ arc4random_buf(buf, sizeof(buf));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+ ATF_CHECK_MSG(localepoch != local->arc4_epoch,
+ "local epoch unchanged from %u", localepoch);
+
+ /*
+ * Create a new thread to grab output from the global state,
+ * wait for it to complete, and verify the global epoch cache
+ * has changed. (Now that we have already used the local state
+ * in this thread, we can't use the global state any more.)
+ */
+ RZ(pthread_create(&thread, NULL, &arc4random_global_thread, NULL));
+ RZ(pthread_join(thread, NULL));
+ ATF_CHECK_MSG(globalepoch != global->arc4_epoch,
+ "global epoch unchanged from %u", globalepoch);
+}
+
+ATF_TC(chroot);
+ATF_TC_HEAD(chroot, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test arc4random in an empty chroot");
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(chroot, tc)
+{
+ pid_t pid;
+ int status;
+
+ /*
+ * Create an empty chroot.
+ */
+ RL(mkdir("root", 0500));
+
+ /*
+ * In a child process, enter the chroot and verify that we
+ * can't open /dev/urandom but we can use arc4random.
+ *
+ * (atf gets unhappy if we chroot in the same process, when it
+ * later tries to create a results file.)
+ */
+ RL(pid = fork());
+ if (pid == 0) {
+ unsigned char buf[32] = {0};
+
+ if (chroot("root") == -1)
+ err(1, "chroot");
+ if (open(_PATH_URANDOM, O_RDONLY) != -1)
+ errx(1, "open /dev/urandom must fail in empty chroot");
+ if (errno != ENOENT) {
+ err(1, "expected open to fail with %d=ENOENT, not %d",
+ ENOENT, errno);
+ }
+ arc4random_buf(buf, sizeof(buf));
+ if (iszero(buf, sizeof(buf))) /* Pr[fail] = 1/2^256 */
+ errx(1, "arc4random returned all-zero");
+ if (arc4random_prng()->arc4_epoch == 0)
+ errx(1, "arc4random failed to observe entropy epoch");
+ _exit(0);
+ }
+
+ /*
+ * Wait for the child process to finish.
+ */
+ RL(waitpid(pid, &status, 0));
+ ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0,
+ "child exited status 0x%x", status);
+}
+
+ATF_TC(fdlimit);
+ATF_TC_HEAD(fdlimit, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test arc4random works even if we have hit the fd limit");
+}
+ATF_TC_BODY(fdlimit, tc)
+{
+ pid_t pid;
+ int status;
+
+ /*
+ * In a child process, clamp down on the file descriptor
+ * resource limit and verify that we can't open /dev/urandom
+ * but we can use arc4random.
+ *
+ * (atf gets unhappy if we chroot in the same process, when it
+ * later tries to create a results file.)
+ */
+ RL(pid = fork());
+ if (pid == 0) {
+ struct rlimit rlim = {.rlim_cur = 0, .rlim_max = 0};
+ unsigned char buf[32] = {0};
+
+ if (setrlimit(RLIMIT_NOFILE, &rlim) == -1)
+ err(1, "setrlimit(RLIMIT_NOFILE)");
+ if (open(_PATH_URANDOM, O_RDONLY) != -1)
+ errx(1, "open must fail with zero RLIMIT_NOFILE");
+ if (errno != EMFILE) {
+ err(1, "expected open to fail with %d=EMFILE, not %d",
+ EMFILE, errno);
+ }
+ arc4random_buf(buf, sizeof(buf));
+ if (iszero(buf, sizeof(buf))) /* Pr[fail] = 1/2^256 */
+ errx(1, "arc4random returned all-zero");
+ if (arc4random_prng()->arc4_epoch == 0)
+ errx(1, "arc4random failed to observe entropy epoch");
+ _exit(0);
+ }
+
+ /*
+ * Wait for the child process to finish.
+ */
+ RL(waitpid(pid, &status, 0));
+ ATF_CHECK_MSG(WIFEXITED(status) && WEXITSTATUS(status) == 0,
+ "child exited status 0x%x", status);
+}
+
+ATF_TC(fork);
+ATF_TC_HEAD(fork, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test fork zeros the state and gets independent state");
+}
+ATF_TC_BODY(fork, tc)
+{
+ unsigned char buf[32];
+ struct arc4random_prng *local, *global = &arc4random_global.prng;
+ struct arc4random_prng childstate;
+ int fd[2];
+ pid_t child, pid;
+ ssize_t nread;
+ int status;
+
+ /*
+ * Get a sample from the global state to make sure the global
+ * state is initialized.
+ */
+ arc4random_global_buf(buf, sizeof(buf));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+ ATF_CHECK(!iszero(&global->arc4_prng, sizeof(global->arc4_prng)));
+ ATF_CHECK(global->arc4_epoch != 0);
+
+ /*
+ * Get a sample from the local state too to make sure the local
+ * state is initialized.
+ */
+ arc4random_buf(buf, sizeof(buf));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+ local = arc4random_prng();
+ ATF_CHECK(!iszero(&local->arc4_prng, sizeof(local->arc4_prng)));
+ ATF_CHECK(local->arc4_epoch != 0);
+
+ /*
+ * Create a pipe to transfer the state from child to parent.
+ */
+ RL(pipe(fd));
+
+ /*
+ * Fork a child.
+ */
+ RL(child = fork());
+ if (child == 0) {
+ status = 0;
+
+ /*
+ * Verify the states have been zero'd on fork.
+ */
+ if (!iszero(local, sizeof(*local))) {
+ fprintf(stderr, "failed to zero local state\n");
+ status = 1;
+ }
+ if (!iszero(global, sizeof(*global))) {
+ fprintf(stderr, "failed to zero global state\n");
+ status = 1;
+ }
+
+ /*
+ * Verify we generate nonzero output.
+ */
+ arc4random_buf(buf, sizeof(buf));
+ if (iszero(buf, sizeof(buf))) {
+ fprintf(stderr, "failed to generate nonzero output\n");
+ status = 1;
+ }
+
+ /*
+ * Share the state to compare with parent.
+ */
+ if ((size_t)write(fd[1], local, sizeof(*local)) !=
+ sizeof(*local)) {
+ fprintf(stderr, "failed to share local state\n");
+ status = 1;
+ }
+ _exit(status);
+ }
+
+ /*
+ * Verify the global state has been zeroed as expected. (This
+ * way it is never available to the child, even shortly after
+ * the fork syscall returns before the atfork handler is
+ * called.)
+ */
+ ATF_CHECK(iszero(global, sizeof(*global)));
+
+ /*
+ * Read the state from the child.
+ */
+ RL(nread = read(fd[0], &childstate, sizeof(childstate)));
+ ATF_CHECK_EQ_MSG(nread, sizeof(childstate),
+ "nread=%zu sizeof(childstate)=%zu", nread, sizeof(childstate));
+
+ /*
+ * Verify the child state is distinct. (The global state has
+ * been zero'd so it's OK it if coincides.) Check again after
+ * we grab another output.
+ */
+ ATF_CHECK(memcmp(local, &childstate, sizeof(*local)) != 0);
+ arc4random_buf(buf, sizeof(buf));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+ ATF_CHECK(memcmp(local, &childstate, sizeof(*local)) != 0);
+
+ /*
+ * Wait for the child to complete and verify it passed.
+ */
+ RL(pid = waitpid(child, &status, 0));
+ ATF_CHECK_EQ_MSG(status, 0, "child exited with nonzero status=%d",
+ status);
+}
+
+ATF_TC(global_aslimit);
+ATF_TC_HEAD(global_aslimit, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test the global state is used when address space limit is hit");
+}
+ATF_TC_BODY(global_aslimit, tc)
+{
+ unsigned char buf[32], buf1[32];
+
+ /*
+ * Get a sample from the global state (and verify it was using
+ * the global state).
+ */
+ arc4random_global_buf(buf, sizeof(buf));
+
+ /*
+ * Verify we got a sample.
+ */
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+
+ /*
+ * Get a sample from whatever state and make sure it wasn't
+ * repeated, which happens only with probability 1/2^256.
+ */
+ arc4random_buf(buf1, sizeof(buf1));
+ ATF_CHECK(!iszero(buf1, sizeof(buf1))); /* Pr[fail] = 1/2^256 */
+ ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0);
+}
+
+ATF_TC(global_threadkeylimit);
+ATF_TC_HEAD(global_threadkeylimit, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test the global state is used we run out of thread keys");
+}
+ATF_TC_BODY(global_threadkeylimit, tc)
+{
+ unsigned char buf[32], buf1[32];
+
+ /*
+ * Get a sample from the global state (and verify it was using
+ * the global state).
+ */
+ arc4random_global_buf(buf, sizeof(buf));
+
+ /*
+ * Verify we got a sample.
+ */
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+
+ /*
+ * Artificially disable the per-thread state, make it an
+ * invalid thread key altogether, and clear the epoch. Make
+ * sure we're using the global PRNG state now.
+ */
+ arc4random_global.per_thread = false;
+ memset(&arc4random_global.thread_key, 0x5a,
+ sizeof(arc4random_global.thread_key));
+ arc4random_global.prng.arc4_epoch = 0;
+ ATF_CHECK(arc4random_prng() == &arc4random_global.prng);
+
+ /*
+ * Get a sample again and make sure it wasn't repeated, which
+ * happens only with probability 1/2^256.
+ */
+ arc4random_buf(buf1, sizeof(buf1));
+ ATF_CHECK(!iszero(buf1, sizeof(buf1))); /* Pr[fail] = 1/2^256 */
+ ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0);
+
+ /*
+ * Verify this had the effect of updating the global epoch,
+ * meaning we used the global state and not the per-thread
+ * state.
+ */
+ ATF_CHECK(arc4random_global.prng.arc4_epoch != 0);
+}
+
+ATF_TC(local);
+ATF_TC_HEAD(local, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test arc4random uses thread-local state");
+ /* XXX skip if libc was built without _REENTRANT */
+}
+ATF_TC_BODY(local, tc)
+{
+ unsigned char buf[32], buf1[32];
+ struct arc4random_prng *prng;
+
+ /*
+ * Get a sample to start things off.
+ */
+ arc4random_buf(buf, sizeof(buf));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+
+ /*
+ * Verify the arc4random state is _not_ the global state.
+ */
+ prng = arc4random_prng();
+ ATF_CHECK(prng != &arc4random_global.prng);
+ ATF_CHECK(!iszero(&prng->arc4_prng, sizeof(prng->arc4_prng)));
+ ATF_CHECK(prng->arc4_epoch != 0);
+
+ /*
+ * Get another sample and make sure it wasn't repeated, which
+ * happens only with probability 1/2^256.
+ */
+ arc4random_buf(buf1, sizeof(buf1));
+ ATF_CHECK(!iszero(buf1, sizeof(buf1))); /* Pr[fail] = 1/2^256 */
+ ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0);
+}
+
+ATF_TC(stackfallback);
+ATF_TC_HEAD(stackfallback, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test arc4random with pthread_atfork and thr_keycreate failure");
+}
+ATF_TC_BODY(stackfallback, tc)
+{
+ unsigned char buf[32], buf1[32];
+ struct arc4random_prng *local;
+
+ /*
+ * Get a sample to start things off. This makes the library
+ * gets initialized.
+ */
+ arc4random_buf(buf, sizeof(buf));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+
+ /*
+ * Clear the arc4random global state, and the local state if it
+ * exists, and pretend pthread_atfork and thr_keycreate had
+ * both failed.
+ */
+ memset(&arc4random_global.prng, 0, sizeof(arc4random_global.prng));
+ if ((local = arc4random_prng()) != NULL)
+ memset(local, 0, sizeof(*local));
+ arc4random_global.forksafe = false;
+ arc4random_global.per_thread = false;
+
+ /*
+ * Make sure it still works to get a sample.
+ */
+ arc4random_buf(buf1, sizeof(buf1));
+ ATF_CHECK(!iszero(buf, sizeof(buf))); /* Pr[fail] = 1/2^256 */
+ ATF_CHECK(memcmp(buf, buf1, sizeof(buf)) != 0);
+
+ /*
+ * Make sure the global and local epochs did not change.
+ */
+ ATF_CHECK_EQ_MSG(arc4random_global.prng.arc4_epoch, 0,
+ "global epoch: %d", arc4random_global.prng.arc4_epoch);
+ if (local != NULL) {
+ ATF_CHECK_EQ_MSG(local->arc4_epoch, 0,
+ "local epoch: %d", local->arc4_epoch);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, addrandom);
+ ATF_TP_ADD_TC(tp, chroot);
+ ATF_TP_ADD_TC(tp, consolidate);
+ ATF_TP_ADD_TC(tp, fdlimit);
+ ATF_TP_ADD_TC(tp, fork);
+ ATF_TP_ADD_TC(tp, global_aslimit);
+ ATF_TP_ADD_TC(tp, global_threadkeylimit);
+ ATF_TP_ADD_TC(tp, local);
+ ATF_TP_ADD_TC(tp, stackfallback);
+
+ return atf_no_error();
+}
diff --git a/lib/libc/gen/t_ctype.c b/lib/libc/gen/t_ctype.c
new file mode 100644
index 000000000000..ee4db34304b7
--- /dev/null
+++ b/lib/libc/gen/t_ctype.c
@@ -0,0 +1,1236 @@
+/* $NetBSD: t_ctype.c,v 1.12 2025/09/15 00:11:55 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Tests for the ctype(3) character classification macros.
+ *
+ * NOTE: These tests intentionally trigger undefined behaviour by
+ * passing int values to the ctype(3) functions which are neither EOF
+ * nor representable by unsigned char. The purpose is to verify
+ * NetBSD's intentional trapping of this undefined behaviour -- or
+ * intentional allowing of this undefined behaviour, when the
+ * environment variable LIBC_ALLOWCTYPEABUSE is set.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_ctype.c,v 1.12 2025/09/15 00:11:55 riastradh Exp $");
+
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <ctype.h>
+#include <locale.h>
+#include <limits.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "h_macros.h"
+
+#ifdef __CHAR_UNSIGNED__
+enum { CHAR_UNSIGNED = 1 };
+#else
+enum { CHAR_UNSIGNED = 0 };
+#endif
+
+/*
+ * libc has a guard page for the LC_CTYPE=C ctype(3) tables only on
+ * some platforms. We skip it if char is unsigned (in which case the
+ * common kind of ctype(3) abuse is unlikely). We also skip it in
+ * static builds -- this is determined in the Makefile.
+ */
+#ifndef _CTYPE_GUARD_PAGE
+# ifdef __CHAR_UNSIGNED__
+# define _CTYPE_GUARD_PAGE 0
+# else
+# define _CTYPE_GUARD_PAGE 1
+# endif
+#endif
+
+static const char *const locales[] = { "C.UTF-8", "fr_FR.ISO8859-1", "C" };
+
+static int isalpha_wrapper(int ch) { return isalpha(ch); }
+static int isupper_wrapper(int ch) { return isupper(ch); }
+static int islower_wrapper(int ch) { return islower(ch); }
+static int isdigit_wrapper(int ch) { return isdigit(ch); }
+static int isxdigit_wrapper(int ch) { return isxdigit(ch); }
+static int isalnum_wrapper(int ch) { return isalnum(ch); }
+static int isspace_wrapper(int ch) { return isspace(ch); }
+static int ispunct_wrapper(int ch) { return ispunct(ch); }
+static int isprint_wrapper(int ch) { return isprint(ch); }
+static int isgraph_wrapper(int ch) { return isgraph(ch); }
+static int iscntrl_wrapper(int ch) { return iscntrl(ch); }
+static int isblank_wrapper(int ch) { return isblank(ch); }
+static int toupper_wrapper(int ch) { return toupper(ch); }
+static int tolower_wrapper(int ch) { return tolower(ch); }
+
+jmp_buf env;
+
+static void
+handle_signal(int signo)
+{
+
+ longjmp(env, 1);
+}
+
+static void
+test_abuse(const char *name, int (*ctypefn)(int))
+{
+ volatile int ch; /* for longjmp */
+
+ for (ch = CHAR_MIN; ch < 0; ch++) {
+ volatile int result;
+
+ if (ch == EOF)
+ continue;
+ ATF_REQUIRE_MSG(ch != (int)(unsigned char)ch, "ch=%d", ch);
+ if (setjmp(env) == 0) {
+ REQUIRE_LIBC(signal(SIGABRT, &handle_signal), SIG_ERR);
+ REQUIRE_LIBC(signal(SIGSEGV, &handle_signal), SIG_ERR);
+ result = (*ctypefn)(ch);
+ REQUIRE_LIBC(signal(SIGABRT, SIG_DFL), SIG_ERR);
+ REQUIRE_LIBC(signal(SIGSEGV, SIG_DFL), SIG_ERR);
+ atf_tc_fail_nonfatal("%s failed to detect invalid %d,"
+ " returned %d",
+ name, ch, result);
+ } else {
+ REQUIRE_LIBC(signal(SIGABRT, SIG_DFL), SIG_ERR);
+ REQUIRE_LIBC(signal(SIGSEGV, SIG_DFL), SIG_ERR);
+ }
+ }
+
+ for (; ch <= CHAR_MAX; ch++)
+ ATF_REQUIRE_MSG(ch == (int)(unsigned char)ch, "ch=%d", ch);
+}
+
+static void
+test_abuse_in_locales(const char *name, int (*ctypefn)(int), bool macro)
+{
+ size_t i;
+
+ for (i = 0; i < __arraycount(locales); i++) {
+ char buf[128];
+
+ if (!_CTYPE_GUARD_PAGE && macro &&
+ strcmp(locales[i], "C") == 0) {
+ fprintf(stderr, "skip LC_CTYPE=C ctype(3) abuse --"
+ " no libc guard page on this platform\n");
+ }
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, locales[i]) != NULL,
+ "locales[i]=%s", locales[i]);
+ snprintf(buf, sizeof(buf), "[%s]%s", locales[i], name);
+ test_abuse(buf, ctypefn);
+ }
+}
+
+static void
+test_use(const char *name, int (*ctypefn)(int))
+{
+ volatile int ch; /* for longjmp */
+
+ for (ch = EOF; ch <= CHAR_MAX; ch = (ch == EOF ? 0 : ch + 1)) {
+ volatile int result;
+
+ if (setjmp(env) == 0) {
+ REQUIRE_LIBC(signal(SIGABRT, &handle_signal), SIG_ERR);
+ REQUIRE_LIBC(signal(SIGSEGV, &handle_signal), SIG_ERR);
+ result = (*ctypefn)(ch);
+ REQUIRE_LIBC(signal(SIGABRT, SIG_DFL), SIG_ERR);
+ REQUIRE_LIBC(signal(SIGSEGV, SIG_DFL), SIG_ERR);
+ (void)result;
+ } else {
+ REQUIRE_LIBC(signal(SIGABRT, SIG_DFL), SIG_ERR);
+ REQUIRE_LIBC(signal(SIGSEGV, SIG_DFL), SIG_ERR);
+ atf_tc_fail_nonfatal("%s(%d) raised SIGSEGV",
+ name, ch);
+ }
+ }
+}
+
+static void
+test_isalpha_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("isalpha", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_isupper_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("isupper", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_islower_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("islower", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_isdigit_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("isdigit", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_isxdigit_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("isxdigit", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_isalnum_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("isalnum", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_isspace_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("isspace", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_ispunct_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("ispunct", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_isprint_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("isprint", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_isgraph_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("isgraph", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_iscntrl_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("iscntrl", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_isblank_locale(const char *L, int (*ctypefn)(int))
+{
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("isblank", ctypefn);
+ ATF_CHECK(!(*ctypefn)(EOF));
+}
+
+static void
+test_toupper_locale(const char *L, int (*ctypefn)(int))
+{
+ int result;
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("toupper", ctypefn);
+ ATF_CHECK_MSG((result = (*ctypefn)(EOF)) == EOF,
+ "result=%d, expected EOF=%d", result, EOF);
+}
+
+static void
+test_tolower_locale(const char *L, int (*ctypefn)(int))
+{
+ int result;
+
+ ATF_REQUIRE_MSG(setlocale(LC_CTYPE, L) != NULL, "L=%s", L);
+ test_use("tolower", ctypefn);
+ ATF_CHECK_MSG((result = (*ctypefn)(EOF)) == EOF,
+ "result=%d, expected EOF=%d", result, EOF);
+}
+
+static void
+test_isalpha_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case 'a': case 'A':
+ case 'b': case 'B':
+ case 'c': case 'C':
+ case 'd': case 'D':
+ case 'e': case 'E':
+ case 'f': case 'F':
+ case 'g': case 'G':
+ case 'h': case 'H':
+ case 'i': case 'I':
+ case 'j': case 'J':
+ case 'k': case 'K':
+ case 'l': case 'L':
+ case 'm': case 'M':
+ case 'n': case 'N':
+ case 'o': case 'O':
+ case 'p': case 'P':
+ case 'q': case 'Q':
+ case 'r': case 'R':
+ case 's': case 'S':
+ case 't': case 'T':
+ case 'u': case 'U':
+ case 'v': case 'V':
+ case 'w': case 'W':
+ case 'x': case 'X':
+ case 'y': case 'Y':
+ case 'z': case 'Z':
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ default:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_isupper_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ default:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_islower_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z':
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ default:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_isdigit_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ default:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_isxdigit_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'a': case 'A':
+ case 'b': case 'B':
+ case 'c': case 'C':
+ case 'd': case 'D':
+ case 'e': case 'E':
+ case 'f': case 'F':
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ default:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_isalnum_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case 'a': case 'A':
+ case 'b': case 'B':
+ case 'c': case 'C':
+ case 'd': case 'D':
+ case 'e': case 'E':
+ case 'f': case 'F':
+ case 'g': case 'G':
+ case 'h': case 'H':
+ case 'i': case 'I':
+ case 'j': case 'J':
+ case 'k': case 'K':
+ case 'l': case 'L':
+ case 'm': case 'M':
+ case 'n': case 'N':
+ case 'o': case 'O':
+ case 'p': case 'P':
+ case 'q': case 'Q':
+ case 'r': case 'R':
+ case 's': case 'S':
+ case 't': case 'T':
+ case 'u': case 'U':
+ case 'v': case 'V':
+ case 'w': case 'W':
+ case 'x': case 'X':
+ case 'y': case 'Y':
+ case 'z': case 'Z':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ default:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_isspace_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case ' ':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ case '\v':
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ default:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_ispunct_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ default:
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ case 0 ... 0x1f:
+ case 0x20: /* space is printing but not punctuation */
+ case 0x7f:
+ case 0x80 ... 0xff:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ case 'a': case 'A':
+ case 'b': case 'B':
+ case 'c': case 'C':
+ case 'd': case 'D':
+ case 'e': case 'E':
+ case 'f': case 'F':
+ case 'g': case 'G':
+ case 'h': case 'H':
+ case 'i': case 'I':
+ case 'j': case 'J':
+ case 'k': case 'K':
+ case 'l': case 'L':
+ case 'm': case 'M':
+ case 'n': case 'N':
+ case 'o': case 'O':
+ case 'p': case 'P':
+ case 'q': case 'Q':
+ case 'r': case 'R':
+ case 's': case 'S':
+ case 't': case 'T':
+ case 'u': case 'U':
+ case 'v': case 'V':
+ case 'w': case 'W':
+ case 'x': case 'X':
+ case 'y': case 'Y':
+ case 'z': case 'Z':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_isprint_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case 0x20: /* space is printing but not graphic */
+ default:
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ case 0 ... 0x1f:
+ case 0x7f:
+ case 0x80 ... 0xff:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_isgraph_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ default:
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ case 0 ... 0x1f:
+ case 0x20: /* space is printing but not graphic */
+ case 0x7f:
+ case 0x80 ... 0xff:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_iscntrl_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case 0 ... 0x1f:
+ case 0x7f:
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ default:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_isblank_c(int (*ctypefn)(int))
+{
+ int ch;
+
+ ATF_CHECK(!(*ctypefn)(EOF));
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case ' ':
+ case '\t':
+ ATF_CHECK_MSG((*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ default:
+ ATF_CHECK_MSG(!(*ctypefn)(ch), "ch=0x%x", ch);
+ break;
+ }
+ }
+}
+
+static void
+test_toupper_c(int (*ctypefn)(int))
+{
+ int ch, result, expected;
+
+ ATF_CHECK_MSG((result = (*ctypefn)(EOF)) == EOF,
+ "result=%d, expected EOF=%d", result, EOF);
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case 'a': case 'A': expected = 'A'; break;
+ case 'b': case 'B': expected = 'B'; break;
+ case 'c': case 'C': expected = 'C'; break;
+ case 'd': case 'D': expected = 'D'; break;
+ case 'e': case 'E': expected = 'E'; break;
+ case 'f': case 'F': expected = 'F'; break;
+ case 'g': case 'G': expected = 'G'; break;
+ case 'h': case 'H': expected = 'H'; break;
+ case 'i': case 'I': expected = 'I'; break;
+ case 'j': case 'J': expected = 'J'; break;
+ case 'k': case 'K': expected = 'K'; break;
+ case 'l': case 'L': expected = 'L'; break;
+ case 'm': case 'M': expected = 'M'; break;
+ case 'n': case 'N': expected = 'N'; break;
+ case 'o': case 'O': expected = 'O'; break;
+ case 'p': case 'P': expected = 'P'; break;
+ case 'q': case 'Q': expected = 'Q'; break;
+ case 'r': case 'R': expected = 'R'; break;
+ case 's': case 'S': expected = 'S'; break;
+ case 't': case 'T': expected = 'T'; break;
+ case 'u': case 'U': expected = 'U'; break;
+ case 'v': case 'V': expected = 'V'; break;
+ case 'w': case 'W': expected = 'W'; break;
+ case 'x': case 'X': expected = 'X'; break;
+ case 'y': case 'Y': expected = 'Y'; break;
+ case 'z': case 'Z': expected = 'Z'; break;
+ default:
+ expected = ch;
+ break;
+ }
+ ATF_CHECK_MSG((result = (*ctypefn)(ch)) == expected,
+ "result=%d expected=%d", result, expected);
+ }
+}
+
+static void
+test_tolower_c(int (*ctypefn)(int))
+{
+ int ch, result, expected;
+
+ ATF_CHECK_MSG((result = (*ctypefn)(EOF)) == EOF,
+ "result=%d, expected EOF=%d", result, EOF);
+ for (ch = 0; ch <= UCHAR_MAX; ch++) {
+ switch (ch) {
+ case 'a': case 'A': expected = 'a'; break;
+ case 'b': case 'B': expected = 'b'; break;
+ case 'c': case 'C': expected = 'c'; break;
+ case 'd': case 'D': expected = 'd'; break;
+ case 'e': case 'E': expected = 'e'; break;
+ case 'f': case 'F': expected = 'f'; break;
+ case 'g': case 'G': expected = 'g'; break;
+ case 'h': case 'H': expected = 'h'; break;
+ case 'i': case 'I': expected = 'i'; break;
+ case 'j': case 'J': expected = 'j'; break;
+ case 'k': case 'K': expected = 'k'; break;
+ case 'l': case 'L': expected = 'l'; break;
+ case 'm': case 'M': expected = 'm'; break;
+ case 'n': case 'N': expected = 'n'; break;
+ case 'o': case 'O': expected = 'o'; break;
+ case 'p': case 'P': expected = 'p'; break;
+ case 'q': case 'Q': expected = 'q'; break;
+ case 'r': case 'R': expected = 'r'; break;
+ case 's': case 'S': expected = 's'; break;
+ case 't': case 'T': expected = 't'; break;
+ case 'u': case 'U': expected = 'u'; break;
+ case 'v': case 'V': expected = 'v'; break;
+ case 'w': case 'W': expected = 'w'; break;
+ case 'x': case 'X': expected = 'x'; break;
+ case 'y': case 'Y': expected = 'y'; break;
+ case 'z': case 'Z': expected = 'z'; break;
+ default:
+ expected = ch;
+ break;
+ }
+ ATF_CHECK_MSG((result = (*ctypefn)(ch)) == expected,
+ "result=%d expected=%d", result, expected);
+ }
+}
+
+extern char **environ;
+
+static void
+test_abuse_override(const struct atf_tc *tc, const char *fn, const char *mode,
+ const char *locale)
+{
+ char h_ctype_abuse[PATH_MAX];
+ pid_t pid;
+ int status;
+
+ RL(snprintf(h_ctype_abuse, sizeof(h_ctype_abuse), "%s/h_ctype_abuse",
+ atf_tc_get_config_var(tc, "srcdir")));
+
+ RL(setenv("LIBC_ALLOWCTYPEABUSE", "", 1));
+
+ RL(pid = vfork());
+ if (pid == 0) { /* child */
+ char *const argv[] = {
+ h_ctype_abuse,
+ __UNCONST(fn),
+ __UNCONST(mode),
+ __UNCONST(locale),
+ NULL,
+ };
+
+ if (execve(argv[0], argv, environ) == -1)
+ _exit(1);
+ _exit(2);
+ }
+
+ RL(waitpid(pid, &status, 0));
+ if (WIFSIGNALED(status)) {
+ atf_tc_fail_nonfatal("child exited on signal %d (%s)",
+ WTERMSIG(status), strsignal(WTERMSIG(status)));
+ } else if (!WIFEXITED(status)) {
+ atf_tc_fail_nonfatal("child exited status=0x%x", status);
+ } else {
+ ATF_CHECK_MSG(WEXITSTATUS(status) == 0,
+ "child exited with code %d",
+ WEXITSTATUS(status));
+ }
+}
+
+static void
+test_abuse_override_in_locales(const struct atf_tc *tc, const char *fn,
+ const char *mode)
+{
+ size_t i;
+
+ for (i = 0; i < __arraycount(locales); i++) {
+ fprintf(stderr, "# locale %s\n", locales[i]);
+ test_abuse_override(tc, fn, mode, locales[i]);
+ }
+}
+
+#define ADD_TEST_ABUSE(TP, FN) do \
+{ \
+ ATF_TP_ADD_TC(TP, abuse_##FN##_macro_c); \
+ ATF_TP_ADD_TC(TP, abuse_##FN##_function_c); \
+ ATF_TP_ADD_TC(TP, abuse_##FN##_macro_locale); \
+ ATF_TP_ADD_TC(TP, abuse_##FN##_function_locale); \
+} while (0)
+
+#define DEF_TEST_ABUSE(FN) \
+ATF_TC(abuse_##FN##_macro_c); \
+ATF_TC_HEAD(abuse_##FN##_macro_c, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test abusing "#FN" macro with default LC_CTYPE=C"); \
+} \
+ATF_TC_BODY(abuse_##FN##_macro_c, tc) \
+{ \
+ if (CHAR_UNSIGNED) { \
+ atf_tc_skip("runtime ctype(3) abuse is impossible with" \
+ " unsigned char"); \
+ } \
+ if (!_CTYPE_GUARD_PAGE) \
+ atf_tc_skip("no LC_CTYPE=C guard page on this platform"); \
+ test_abuse(#FN, &FN##_wrapper); \
+} \
+ATF_TC(abuse_##FN##_function_c); \
+ATF_TC_HEAD(abuse_##FN##_function_c, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test abusing "#FN" function with default LC_CTYPE=C"); \
+} \
+ATF_TC_BODY(abuse_##FN##_function_c, tc) \
+{ \
+ if (CHAR_UNSIGNED) { \
+ atf_tc_skip("runtime ctype(3) abuse is impossible with" \
+ " unsigned char"); \
+ } \
+ if (!_CTYPE_GUARD_PAGE) \
+ atf_tc_skip("no LC_CTYPE=C guard page on this platform"); \
+ test_abuse(#FN, &FN); \
+} \
+ATF_TC(abuse_##FN##_macro_locale); \
+ATF_TC_HEAD(abuse_##FN##_macro_locale, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test abusing "#FN" macro with locales"); \
+} \
+ATF_TC_BODY(abuse_##FN##_macro_locale, tc) \
+{ \
+ if (CHAR_UNSIGNED) { \
+ atf_tc_skip("runtime ctype(3) abuse is impossible with" \
+ " unsigned char"); \
+ } \
+ test_abuse_in_locales(#FN, &FN##_wrapper, /*macro*/true); \
+} \
+ATF_TC(abuse_##FN##_function_locale); \
+ATF_TC_HEAD(abuse_##FN##_function_locale, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test abusing "#FN" function with locales"); \
+} \
+ATF_TC_BODY(abuse_##FN##_function_locale, tc) \
+{ \
+ if (CHAR_UNSIGNED) { \
+ atf_tc_skip("runtime ctype(3) abuse is impossible with" \
+ " unsigned char"); \
+ } \
+ test_abuse_in_locales(#FN, &FN, /*macro*/false); \
+}
+
+#define ADD_TEST_ABUSE_OVERRIDE(TP, FN) do \
+{ \
+ ATF_TP_ADD_TC(TP, abuse_override_##FN##_macro_c); \
+ ATF_TP_ADD_TC(TP, abuse_override_##FN##_function_c); \
+ ATF_TP_ADD_TC(TP, abuse_override_##FN##_macro_locale); \
+ ATF_TP_ADD_TC(TP, abuse_override_##FN##_function_locale); \
+} while (0)
+
+#define DEF_TEST_ABUSE_OVERRIDE(FN) \
+ATF_TC(abuse_override_##FN##_macro_c); \
+ATF_TC_HEAD(abuse_override_##FN##_macro_c, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test allowing abuse of "#FN" macro with default LC_CTYPE=C"); \
+} \
+ATF_TC_BODY(abuse_override_##FN##_macro_c, tc) \
+{ \
+ test_abuse_override(tc, #FN, "macro", NULL); \
+} \
+ATF_TC(abuse_override_##FN##_function_c); \
+ATF_TC_HEAD(abuse_override_##FN##_function_c, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test allowing abuse "#FN" function with default LC_CTYPE=C"); \
+} \
+ATF_TC_BODY(abuse_override_##FN##_function_c, tc) \
+{ \
+ test_abuse_override(tc, #FN, "function", NULL); \
+} \
+ATF_TC(abuse_override_##FN##_macro_locale); \
+ATF_TC_HEAD(abuse_override_##FN##_macro_locale, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test allowing abuse of "#FN" macro with locales"); \
+} \
+ATF_TC_BODY(abuse_override_##FN##_macro_locale, tc) \
+{ \
+ test_abuse_override_in_locales(tc, #FN, "macro"); \
+} \
+ATF_TC(abuse_override_##FN##_function_locale); \
+ATF_TC_HEAD(abuse_override_##FN##_function_locale, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test allowing abuse of "#FN" function with locales"); \
+} \
+ATF_TC_BODY(abuse_override_##FN##_function_locale, tc) \
+{ \
+ test_abuse_override_in_locales(tc, #FN, "function"); \
+}
+
+#define ADD_TEST_USE(TP, FN) do \
+{ \
+ ATF_TP_ADD_TC(TP, FN##_macro_c); \
+ ATF_TP_ADD_TC(TP, FN##_function_c); \
+ ATF_TP_ADD_TC(TP, FN##_macro_locale); \
+ ATF_TP_ADD_TC(TP, FN##_function_locale); \
+} while (0)
+
+#define DEF_TEST_USE(FN) \
+ATF_TC(FN##_macro_c); \
+ATF_TC_HEAD(FN##_macro_c, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test "#FN" macro with default LC_CTYPE=C"); \
+} \
+ATF_TC_BODY(FN##_macro_c, tc) \
+{ \
+ test_##FN##_c(&FN##_wrapper); \
+} \
+ATF_TC(FN##_function_c); \
+ATF_TC_HEAD(FN##_function_c, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test "#FN" function with default LC_CTYPE=C"); \
+} \
+ATF_TC_BODY(FN##_function_c, tc) \
+{ \
+ test_##FN##_c(&FN); \
+} \
+ATF_TC(FN##_macro_locale); \
+ATF_TC_HEAD(FN##_macro_locale, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test "#FN" macro with locales"); \
+} \
+ATF_TC_BODY(FN##_macro_locale, tc) \
+{ \
+ size_t i; \
+ \
+ for (i = 0; i < __arraycount(locales); i++) \
+ test_##FN##_locale(locales[i], &FN##_wrapper); \
+} \
+ATF_TC(FN##_function_locale); \
+ATF_TC_HEAD(FN##_function_locale, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", \
+ "Test "#FN" function with locales"); \
+} \
+ATF_TC_BODY(FN##_function_locale, tc) \
+{ \
+ size_t i; \
+ \
+ for (i = 0; i < __arraycount(locales); i++) \
+ test_##FN##_locale(locales[i], &FN); \
+}
+
+DEF_TEST_ABUSE(isalpha)
+DEF_TEST_ABUSE(isupper)
+DEF_TEST_ABUSE(islower)
+DEF_TEST_ABUSE(isdigit)
+DEF_TEST_ABUSE(isxdigit)
+DEF_TEST_ABUSE(isalnum)
+DEF_TEST_ABUSE(isspace)
+DEF_TEST_ABUSE(ispunct)
+DEF_TEST_ABUSE(isprint)
+DEF_TEST_ABUSE(isgraph)
+DEF_TEST_ABUSE(iscntrl)
+DEF_TEST_ABUSE(isblank)
+DEF_TEST_ABUSE(toupper)
+DEF_TEST_ABUSE(tolower)
+
+DEF_TEST_ABUSE_OVERRIDE(isalpha)
+DEF_TEST_ABUSE_OVERRIDE(isupper)
+DEF_TEST_ABUSE_OVERRIDE(islower)
+DEF_TEST_ABUSE_OVERRIDE(isdigit)
+DEF_TEST_ABUSE_OVERRIDE(isxdigit)
+DEF_TEST_ABUSE_OVERRIDE(isalnum)
+DEF_TEST_ABUSE_OVERRIDE(isspace)
+DEF_TEST_ABUSE_OVERRIDE(ispunct)
+DEF_TEST_ABUSE_OVERRIDE(isprint)
+DEF_TEST_ABUSE_OVERRIDE(isgraph)
+DEF_TEST_ABUSE_OVERRIDE(iscntrl)
+DEF_TEST_ABUSE_OVERRIDE(isblank)
+DEF_TEST_ABUSE_OVERRIDE(toupper)
+DEF_TEST_ABUSE_OVERRIDE(tolower)
+
+DEF_TEST_USE(isalpha)
+DEF_TEST_USE(isupper)
+DEF_TEST_USE(islower)
+DEF_TEST_USE(isdigit)
+DEF_TEST_USE(isxdigit)
+DEF_TEST_USE(isalnum)
+DEF_TEST_USE(isspace)
+DEF_TEST_USE(ispunct)
+DEF_TEST_USE(isprint)
+DEF_TEST_USE(isgraph)
+DEF_TEST_USE(iscntrl)
+DEF_TEST_USE(isblank)
+DEF_TEST_USE(toupper)
+DEF_TEST_USE(tolower)
+
+ATF_TC(eof_confusion_iso8859_1);
+ATF_TC_HEAD(eof_confusion_iso8859_1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test potential confusion with EOF in ISO-8859-1");
+}
+ATF_TC_BODY(eof_confusion_iso8859_1, tc)
+{
+ int ydots = 0xff; /* ÿ, LATIN SMALL LETTER Y WITH DIAERESIS */
+ int ch;
+
+ /*
+ * The LATIN SMALL LETTER Y WITH DIAERESIS code point 0xff in
+ * ISO-8859-1 is curious primarily because its bit pattern
+ * coincides with an 8-bit signed -1, which is to say, EOF as
+ * an 8-bit quantity; of course, for EOF, all of the is*
+ * functions are supposed to return false (as we test above).
+ * It also has the curious property that it lacks any
+ * corresponding uppercase code point in ISO-8859-1, so we
+ * can't distinguish it from EOF by tolower/toupper.
+ */
+ ATF_REQUIRE(setlocale(LC_CTYPE, "fr_FR.ISO8859-1") != NULL);
+ ATF_CHECK(isalpha(ydots));
+ ATF_CHECK(!isupper(ydots));
+ ATF_CHECK(islower(ydots));
+ ATF_CHECK(!isdigit(ydots));
+ ATF_CHECK(!isxdigit(ydots));
+ ATF_CHECK(isalnum(ydots));
+ ATF_CHECK(!isspace(ydots));
+ ATF_CHECK(!ispunct(ydots));
+ ATF_CHECK(isprint(ydots));
+ ATF_CHECK(isgraph(ydots));
+ ATF_CHECK(!iscntrl(ydots));
+ ATF_CHECK(!isblank(ydots));
+ ATF_CHECK_MSG((ch = toupper(ydots)) == ydots, "ch=0x%x", ch);
+ ATF_CHECK_MSG((ch = tolower(ydots)) == ydots, "ch=0x%x", ch);
+}
+
+ATF_TC(eof_confusion_koi8_u);
+ATF_TC_HEAD(eof_confusion_koi8_u, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test potential confusion with EOF in KOI8-U");
+}
+ATF_TC_BODY(eof_confusion_koi8_u, tc)
+{
+ int Hard = 0xff; /* Ъ, CYRILLIC CAPITAL LETTER HARD SIGN */
+ int hard = 0xdf; /* ъ, CYRILLIC SMALL LETTER HARD SIGN */
+ int ch;
+
+ /*
+ * The CYRILLIC CAPITAL LETTER HARD SIGN code point 0xff in
+ * KOI8-U (and KOI8-R) also coincides with the bit pattern of
+ * an 8-bit signed -1. Unlike LATIN SMALL LETTER Y WITH
+ * DIAERESIS, it has a lowercase equivalent in KOI8-U.
+ */
+ ATF_REQUIRE(setlocale(LC_CTYPE, "uk_UA.KOI8-U") != NULL);
+ ATF_CHECK(isalpha(Hard));
+ ATF_CHECK(isupper(Hard));
+ ATF_CHECK(!islower(Hard));
+ ATF_CHECK(!isdigit(Hard));
+ ATF_CHECK(!isxdigit(Hard));
+ ATF_CHECK(isalnum(Hard));
+ ATF_CHECK(!isspace(Hard));
+ ATF_CHECK(!ispunct(Hard));
+ ATF_CHECK(isprint(Hard));
+ ATF_CHECK(isgraph(Hard));
+ ATF_CHECK(!iscntrl(Hard));
+ ATF_CHECK(!isblank(Hard));
+ ATF_CHECK_MSG((ch = toupper(Hard)) == Hard, "ch=0x%x", ch);
+ ATF_CHECK_MSG((ch = tolower(Hard)) == hard, "ch=0x%x", ch);
+}
+
+ATF_TC(eof_confusion_pt154);
+ATF_TC_HEAD(eof_confusion_pt154, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test potential confusion with EOF in PT154");
+}
+ATF_TC_BODY(eof_confusion_pt154, tc)
+{
+ int ya = 0xff; /* я, CYRILLIC SMALL LETTER YA */
+ int Ya = 0xdf; /* Я, CYRILLIC CAPITAL LETTER YA */
+ int ch;
+
+ /*
+ * The CYRILLIC SMALL LETTER YA code point 0xff in PT154 also
+ * coincides with the bit pattern of an 8-bit signed -1, and is
+ * lowercase with a corresponding uppercase code point in
+ * PT154.
+ */
+ ATF_REQUIRE(setlocale(LC_CTYPE, "kk_KZ.PT154") != NULL);
+ ATF_CHECK(isalpha(ya));
+ ATF_CHECK(!isupper(ya));
+ ATF_CHECK(islower(ya));
+ ATF_CHECK(!isdigit(ya));
+ ATF_CHECK(!isxdigit(ya));
+ ATF_CHECK(isalnum(ya));
+ ATF_CHECK(!isspace(ya));
+ ATF_CHECK(!ispunct(ya));
+ ATF_CHECK(isprint(ya));
+ ATF_CHECK(isgraph(ya));
+ ATF_CHECK(!iscntrl(ya));
+ ATF_CHECK(!isblank(ya));
+ ATF_CHECK_MSG((ch = toupper(ya)) == Ya, "ch=0x%x", ch);
+ ATF_CHECK_MSG((ch = tolower(ya)) == ya, "ch=0x%x", ch);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ADD_TEST_ABUSE(tp, isalpha);
+ ADD_TEST_ABUSE(tp, isupper);
+ ADD_TEST_ABUSE(tp, islower);
+ ADD_TEST_ABUSE(tp, isdigit);
+ ADD_TEST_ABUSE(tp, isxdigit);
+ ADD_TEST_ABUSE(tp, isalnum);
+ ADD_TEST_ABUSE(tp, isspace);
+ ADD_TEST_ABUSE(tp, ispunct);
+ ADD_TEST_ABUSE(tp, isprint);
+ ADD_TEST_ABUSE(tp, isgraph);
+ ADD_TEST_ABUSE(tp, iscntrl);
+ ADD_TEST_ABUSE(tp, isblank);
+ ADD_TEST_ABUSE(tp, toupper);
+ ADD_TEST_ABUSE(tp, tolower);
+
+ ADD_TEST_ABUSE_OVERRIDE(tp, isalpha);
+ ADD_TEST_ABUSE_OVERRIDE(tp, isupper);
+ ADD_TEST_ABUSE_OVERRIDE(tp, islower);
+ ADD_TEST_ABUSE_OVERRIDE(tp, isdigit);
+ ADD_TEST_ABUSE_OVERRIDE(tp, isxdigit);
+ ADD_TEST_ABUSE_OVERRIDE(tp, isalnum);
+ ADD_TEST_ABUSE_OVERRIDE(tp, isspace);
+ ADD_TEST_ABUSE_OVERRIDE(tp, ispunct);
+ ADD_TEST_ABUSE_OVERRIDE(tp, isprint);
+ ADD_TEST_ABUSE_OVERRIDE(tp, isgraph);
+ ADD_TEST_ABUSE_OVERRIDE(tp, iscntrl);
+ ADD_TEST_ABUSE_OVERRIDE(tp, isblank);
+ ADD_TEST_ABUSE_OVERRIDE(tp, toupper);
+ ADD_TEST_ABUSE_OVERRIDE(tp, tolower);
+
+ ADD_TEST_USE(tp, isalpha);
+ ADD_TEST_USE(tp, isupper);
+ ADD_TEST_USE(tp, islower);
+ ADD_TEST_USE(tp, isdigit);
+ ADD_TEST_USE(tp, isxdigit);
+ ADD_TEST_USE(tp, isalnum);
+ ADD_TEST_USE(tp, isspace);
+ ADD_TEST_USE(tp, ispunct);
+ ADD_TEST_USE(tp, isprint);
+ ADD_TEST_USE(tp, isgraph);
+ ADD_TEST_USE(tp, iscntrl);
+ ADD_TEST_USE(tp, isblank);
+ ADD_TEST_USE(tp, toupper);
+ ADD_TEST_USE(tp, tolower);
+
+ ATF_TP_ADD_TC(tp, eof_confusion_iso8859_1);
+ ATF_TP_ADD_TC(tp, eof_confusion_koi8_u);
+ ATF_TP_ADD_TC(tp, eof_confusion_pt154);
+
+ return atf_no_error();
+}
diff --git a/lib/libc/gen/t_timespec_get.c b/lib/libc/gen/t_timespec_get.c
new file mode 100644
index 000000000000..b51625311032
--- /dev/null
+++ b/lib/libc/gen/t_timespec_get.c
@@ -0,0 +1,123 @@
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nia Alarie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <atf-c.h>
+
+#include <limits.h>
+#include <time.h>
+
+ATF_TC(timespec_getres);
+ATF_TC_HEAD(timespec_getres, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Resolution tests for timespec_getres");
+}
+
+ATF_TC_BODY(timespec_getres, tc)
+{
+ struct timespec ts, ts2;
+
+ ATF_REQUIRE_EQ(timespec_getres(&ts, TIME_MONOTONIC), TIME_MONOTONIC);
+ ATF_REQUIRE_EQ(clock_getres(CLOCK_MONOTONIC, &ts2), 0);
+
+ ATF_REQUIRE_EQ(ts.tv_sec, ts2.tv_sec);
+ ATF_REQUIRE_EQ(ts.tv_nsec, ts2.tv_nsec);
+
+ ATF_REQUIRE_EQ(timespec_getres(&ts, TIME_UTC), TIME_UTC);
+ ATF_REQUIRE_EQ(clock_getres(CLOCK_REALTIME, &ts2), 0);
+
+ ATF_REQUIRE_EQ(ts.tv_sec, ts2.tv_sec);
+ ATF_REQUIRE_EQ(ts.tv_nsec, ts2.tv_nsec);
+
+ /* now an invalid value */
+ ATF_REQUIRE_EQ(timespec_getres(&ts, INT_MAX), 0);
+}
+
+ATF_TC(timespec_get);
+ATF_TC_HEAD(timespec_get, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Basic tests for timespec_get");
+}
+
+ATF_TC_BODY(timespec_get, tc)
+{
+ struct timespec ts, ts2;
+
+ ATF_REQUIRE_EQ(timespec_get(&ts, TIME_UTC), TIME_UTC);
+ ATF_REQUIRE_EQ(clock_gettime(CLOCK_REALTIME, &ts2), 0);
+
+ /*
+ * basically test that these clocks (which should be the same source)
+ * aren't too wildly apart...
+ */
+
+ if (ts2.tv_sec >= ts.tv_sec) {
+ ATF_REQUIRE((ts2.tv_sec - ts.tv_sec) < 86400);
+ } else {
+ ATF_REQUIRE((ts.tv_sec - ts2.tv_sec) < 86400);
+ }
+
+ /* now an invalid value */
+ ATF_REQUIRE_EQ(timespec_get(&ts, INT_MAX), 0);
+}
+
+ATF_TC(timespec_get_monotonic);
+ATF_TC_HEAD(timespec_get_monotonic, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Monotonic tests for timespec_getres");
+}
+
+ATF_TC_BODY(timespec_get_monotonic, tc)
+{
+ struct timespec ts, ts2;
+
+ ATF_REQUIRE_EQ(timespec_get(&ts, TIME_MONOTONIC), TIME_MONOTONIC);
+ ATF_REQUIRE_EQ(clock_gettime(CLOCK_MONOTONIC, &ts2), 0);
+
+ /*
+ * basically test that these clocks (which should be the same source)
+ * aren't too wildly apart...
+ */
+
+ ATF_REQUIRE(ts2.tv_sec >= ts.tv_sec);
+ ATF_REQUIRE((ts2.tv_sec - ts.tv_sec) < 86400);
+
+ /* test that it's actually monotonic */
+ ATF_REQUIRE_EQ(timespec_get(&ts2, TIME_MONOTONIC), TIME_MONOTONIC);
+ ATF_REQUIRE(ts2.tv_sec >= ts.tv_sec);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, timespec_getres);
+ ATF_TP_ADD_TC(tp, timespec_get);
+ ATF_TP_ADD_TC(tp, timespec_get_monotonic);
+
+ return atf_no_error();
+}
+
diff --git a/lib/libc/locale/t_c16rtomb.c b/lib/libc/locale/t_c16rtomb.c
new file mode 100644
index 000000000000..3d695db8dc97
--- /dev/null
+++ b/lib/libc/locale/t_c16rtomb.c
@@ -0,0 +1,287 @@
+/* $NetBSD: t_c16rtomb.c,v 1.6 2024/08/19 16:22:10 riastradh Exp $ */
+
+/*-
+ * 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 <sys/cdefs.h>
+__RCSID("$NetBSD: t_c16rtomb.c,v 1.6 2024/08/19 16:22:10 riastradh Exp $");
+
+#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_EQ_MSG(strcmp(lc_ctype_set, locale_name), 0,
+ "lc_ctype_set=%s locale_name=%s", lc_ctype_set, locale_name);
+}
+
+static mbstate_t s;
+static char buf[7*MB_LEN_MAX + 1];
+
+ATF_TC_WITHOUT_HEAD(c16rtomb_c_locale_test);
+ATF_TC_BODY(c16rtomb_c_locale_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("C");
+
+ /*
+ * If the buffer argument is NULL, c16 is implicitly 0,
+ * c16rtomb() resets its internal state.
+ */
+ ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, L'\0', NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, 0xdc00, NULL)), 1, "n=%zu", n);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0, &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 0 &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, L'\0', NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, L'A', NULL)), 1, "n=%zu", n);
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, L'A', &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 'A' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ /* Unicode character 'Pile of poo'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xd83d, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xdca9, &s)), (size_t)-1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno);
+ ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]);
+
+ /* Incomplete Unicode character 'Pile of poo', interrupted by NUL. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xd83d, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, L'\0', &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+}
+
+ATF_TC_WITHOUT_HEAD(c16rtomb_iso2022jp_locale_test);
+ATF_TC_BODY(c16rtomb_iso2022jp_locale_test, tc)
+{
+ char *p;
+ size_t n;
+
+ require_lc_ctype("ja_JP.ISO-2022-JP");
+
+ /*
+ * If the buffer argument is NULL, c16 is implicitly 0,
+ * c16rtomb() resets its internal state.
+ */
+ ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, L'\0', NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, 0xdc00, NULL)), 1, "n=%zu", n);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0, &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 0 &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, L'\0', NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c16rtomb(NULL, L'A', NULL)), 1, "n=%zu", n);
+
+ /*
+ * 1. U+0042 LATIN CAPITAL LETTER A
+ * 2. U+00A5 YEN SIGN
+ * 3. U+00A5 YEN SIGN (again, no shift needed)
+ * 4. U+30A2 KATAKANA LETTER A
+ * 5. U+30A2 KATAKANA LETTER A (again, no shift needed)
+ * 6. incomplete UTF-16 surrogate pair -- no output
+ * 7. U+0000 NUL (plus shift sequence to initial state)
+ */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ p = buf;
+ ATF_CHECK_EQ_MSG((n = c16rtomb(p, L'A', &s)), 1, "n=%zu", n); /* 1 */
+ p += 1;
+ ATF_CHECK_EQ_MSG((n = c16rtomb(p, 0xa5, &s)), 4, "n=%zu", n); /* 2 */
+ p += 4;
+ ATF_CHECK_EQ_MSG((n = c16rtomb(p, 0xa5, &s)), 1, "n=%zu", n); /* 3 */
+ p += 1;
+ ATF_CHECK_EQ_MSG((n = c16rtomb(p, 0x30a2, &s)), 5, "n=%zu", n); /* 4 */
+ p += 5;
+ ATF_CHECK_EQ_MSG((n = c16rtomb(p, 0x30a2, &s)), 2, "n=%zu", n); /* 5 */
+ p += 2;
+ ATF_CHECK_EQ_MSG((n = c16rtomb(p, 0xd800, &s)), 0, "n=%zu", n); /* 6 */
+ ATF_CHECK_EQ_MSG((n = c16rtomb(p, L'\0', &s)), 4, "n=%zu", n); /* 7 */
+ p += 4;
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 'A' &&
+ (unsigned char)buf[1] == 0x1b && /* shift ISO/IEC 646:JP */
+ (unsigned char)buf[2] == '(' &&
+ (unsigned char)buf[3] == 'J' &&
+ (unsigned char)buf[4] == 0x5c && /* YEN SIGN */
+ (unsigned char)buf[5] == 0x5c && /* YEN SIGN */
+ (unsigned char)buf[6] == 0x1b && /* shift JIS X 0208 */
+ (unsigned char)buf[7] == '$' &&
+ (unsigned char)buf[8] == 'B' &&
+ (unsigned char)buf[9] == 0x25 && /* KATAKANA LETTER A */
+ (unsigned char)buf[10] == 0x22 &&
+ (unsigned char)buf[11] == 0x25 && /* KATAKANA LETTER A */
+ (unsigned char)buf[12] == 0x22 &&
+ (unsigned char)buf[13] == 0x1b && /* shift US-ASCII */
+ (unsigned char)buf[14] == '(' &&
+ (unsigned char)buf[15] == 'B' &&
+ (unsigned char)buf[16] == '\0' &&
+ (unsigned char)buf[17] == 0xcc),
+ "buf=[%02x %02x %02x %02x %02x %02x %02x %02x "
+ " %02x %02x %02x %02x %02x %02x %02x %02x "
+ " %02x %02x]",
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7],
+ buf[8], buf[9], buf[10], buf[11],
+ buf[12], buf[13], buf[14], buf[15],
+ buf[16], buf[17]);
+}
+
+ATF_TC_WITHOUT_HEAD(c16rtomb_iso_8859_1_test);
+ATF_TC_BODY(c16rtomb_iso_8859_1_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.ISO8859-1");
+
+ /* Unicode character 'Euro sign'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0x20ac, &s)), (size_t)-1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno);
+ ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]);
+}
+
+ATF_TC_WITHOUT_HEAD(c16rtomb_iso_8859_15_test);
+ATF_TC_BODY(c16rtomb_iso_8859_15_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.ISO8859-15");
+
+ /* Unicode character 'Euro sign'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0x20ac, &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 0xa4 &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+}
+
+ATF_TC_WITHOUT_HEAD(c16rtomb_utf_8_test);
+ATF_TC_BODY(c16rtomb_utf_8_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.UTF-8");
+
+ /* Unicode character 'Pile of poo'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xd83d, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xdca9, &s)), 4, "n=%zu", n);
+ ATF_CHECK_MSG(((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),
+ "buf=[%02x %02x %02x %02x %02x]",
+ buf[0], buf[1], buf[2], buf[3], buf[4]);
+
+ /* Invalid code; 'Pile of poo' without the trail surrogate. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xd83d, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, L'A', &s)), (size_t)-1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno);
+ ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]);
+
+ /* Invalid code; 'Pile of poo' without the lead surrogate. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xdca9, &s)), (size_t)-1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno);
+ ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]);
+
+ /* Incomplete Unicode character 'Pile of poo', interrupted by NUL. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, 0xd83d, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c16rtomb(buf, L'\0', &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, c16rtomb_c_locale_test);
+ ATF_TP_ADD_TC(tp, c16rtomb_iso2022jp_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/locale/t_c32rtomb.c b/lib/libc/locale/t_c32rtomb.c
new file mode 100644
index 000000000000..7233e14f7967
--- /dev/null
+++ b/lib/libc/locale/t_c32rtomb.c
@@ -0,0 +1,60 @@
+/* $NetBSD: t_c32rtomb.c,v 1.1 2024/08/15 14:16:34 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_c32rtomb.c,v 1.1 2024/08/15 14:16:34 riastradh Exp $");
+
+#include <atf-c.h>
+#include <locale.h>
+#include <uchar.h>
+
+#include "h_macros.h"
+
+ATF_TC(c32rtomb_null);
+ATF_TC_HEAD(c32rtomb_null, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test null string output to c32rtomb");
+}
+ATF_TC_BODY(c32rtomb_null, tc)
+{
+ char *locale;
+ mbstate_t ps = {0};
+ size_t n;
+
+ REQUIRE_LIBC((locale = setlocale(LC_ALL, "C")), NULL);
+ ATF_REQUIRE_EQ_MSG(strcmp(locale, "C"), 0, "locale=%s", locale);
+
+ ATF_CHECK_EQ_MSG((n = c32rtomb(NULL, L'x', &ps)), 1, "n=%zu", n);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, c32rtomb_null);
+ return atf_no_error();
+}
diff --git a/lib/libc/locale/t_c8rtomb.c b/lib/libc/locale/t_c8rtomb.c
new file mode 100644
index 000000000000..ce03e6ed1d20
--- /dev/null
+++ b/lib/libc/locale/t_c8rtomb.c
@@ -0,0 +1,355 @@
+/* $NetBSD: t_c8rtomb.c,v 1.7 2024/08/19 16:22:10 riastradh Exp $ */
+
+/*-
+ * 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 c8rtomb() as specified by C23.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_c8rtomb.c,v 1.7 2024/08/19 16:22:10 riastradh Exp $");
+
+#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_EQ_MSG(strcmp(lc_ctype_set, locale_name), 0,
+ "lc_ctype_set=%s locale_name=%s", lc_ctype_set, locale_name);
+}
+
+static mbstate_t s;
+static char buf[7*MB_LEN_MAX + 1];
+
+ATF_TC_WITHOUT_HEAD(c8rtomb_c_locale_test);
+ATF_TC_BODY(c8rtomb_c_locale_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("C");
+
+ /*
+ * If the buffer argument is NULL, c8 is implicitly 0,
+ * c8rtomb() resets its internal state.
+ */
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, '\0', NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0x80, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xc0, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xe0, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xf0, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xf8, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xfc, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xfe, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xff, NULL)), 1, "n=%zu", n);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0, &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 0 &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, '\0', NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 'A', NULL)), 1, "n=%zu", n);
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 'A', &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 'A' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ /* Unicode character 'Pile of poo'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x92, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xa9, &s)), (size_t)-1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno);
+ ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]);
+
+ /* Incomplete Unicode character 'Pile of poo', interrupted by NUL. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, '\0', &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, '\0', &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x92, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, '\0', &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+}
+
+ATF_TC_WITHOUT_HEAD(c8rtomb_iso2022jp_locale_test);
+ATF_TC_BODY(c8rtomb_iso2022jp_locale_test, tc)
+{
+ char *p;
+ size_t n;
+
+ require_lc_ctype("ja_JP.ISO-2022-JP");
+
+ /*
+ * If the buffer argument is NULL, c8 is implicitly 0,
+ * c8rtomb() resets its internal state.
+ */
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, '\0', NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0x80, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xc0, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xe0, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xf0, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xf8, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xfc, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xfe, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 0xff, NULL)), 1, "n=%zu", n);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0, &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 0 &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, '\0', NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(NULL, 'A', NULL)), 1, "n=%zu", n);
+
+ /*
+ * 1. U+0042 LATIN CAPITAL LETTER A
+ * 2. U+00A5 YEN SIGN
+ * 3. U+00A5 YEN SIGN (again, no shift needed)
+ * 4. U+30A2 KATAKANA LETTER A
+ * 5. U+30A2 KATAKANA LETTER A (again, no shift needed)
+ * 6. incomplete UTF-8 multibyte sequence -- no output
+ * 7. U+0000 NUL (plus shift sequence to initial state)
+ */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ p = buf;
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 'A', &s)), 1, "n=%zu", n); /* 1 */
+ p += 1;
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xc2, &s)), 0, "n=%zu", n); /* 2 */
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xa5, &s)), 4, "n=%zu", n);
+ p += 4;
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xc2, &s)), 0, "n=%zu", n); /* 3 */
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xa5, &s)), 1, "n=%zu", n);
+ p += 1;
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xe3, &s)), 0, "n=%zu", n); /* 4 */
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0x82, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xa2, &s)), 5, "n=%zu", n);
+ p += 5;
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xe3, &s)), 0, "n=%zu", n); /* 5 */
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0x82, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xa2, &s)), 2, "n=%zu", n);
+ p += 2;
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0xe3, &s)), 0, "n=%zu", n); /* 6 */
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, 0x82, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(p, '\0', &s)), 4, "n=%zu", n); /* 7 */
+ p += 4;
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 'A' &&
+ (unsigned char)buf[1] == 0x1b && /* shift ISO/IEC 646:JP */
+ (unsigned char)buf[2] == '(' &&
+ (unsigned char)buf[3] == 'J' &&
+ (unsigned char)buf[4] == 0x5c && /* YEN SIGN */
+ (unsigned char)buf[5] == 0x5c && /* YEN SIGN */
+ (unsigned char)buf[6] == 0x1b && /* shift JIS X 0208 */
+ (unsigned char)buf[7] == '$' &&
+ (unsigned char)buf[8] == 'B' &&
+ (unsigned char)buf[9] == 0x25 && /* KATAKANA LETTER A */
+ (unsigned char)buf[10] == 0x22 &&
+ (unsigned char)buf[11] == 0x25 && /* KATAKANA LETTER A */
+ (unsigned char)buf[12] == 0x22 &&
+ (unsigned char)buf[13] == 0x1b && /* shift US-ASCII */
+ (unsigned char)buf[14] == '(' &&
+ (unsigned char)buf[15] == 'B' &&
+ (unsigned char)buf[16] == '\0' &&
+ (unsigned char)buf[17] == 0xcc),
+ "buf=[%02x %02x %02x %02x %02x %02x %02x %02x "
+ " %02x %02x %02x %02x %02x %02x %02x %02x "
+ " %02x %02x]",
+ buf[0], buf[1], buf[2], buf[3],
+ buf[4], buf[5], buf[6], buf[7],
+ buf[8], buf[9], buf[10], buf[11],
+ buf[12], buf[13], buf[14], buf[15],
+ buf[16], buf[17]);
+}
+
+ATF_TC_WITHOUT_HEAD(c8rtomb_iso_8859_1_test);
+ATF_TC_BODY(c8rtomb_iso_8859_1_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.ISO8859-1");
+
+ /* Unicode character 'Euro sign'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xe2, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x82, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xac, &s)), (size_t)-1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno);
+ ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]);
+}
+
+ATF_TC_WITHOUT_HEAD(c8rtomb_iso_8859_15_test);
+ATF_TC_BODY(c8rtomb_iso_8859_15_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.ISO8859-15");
+
+ /* Unicode character 'Euro sign'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xe2, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x82, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xac, &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == 0xa4 &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+}
+
+ATF_TC_WITHOUT_HEAD(c8rtomb_utf_8_test);
+ATF_TC_BODY(c8rtomb_utf_8_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.UTF-8");
+
+ /* Unicode character 'Pile of poo'. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x92, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xa9, &s)), 4, "n=%zu", n);
+ ATF_CHECK_MSG(((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),
+ "buf=[%02x %02x %02x %02x %02x]",
+ buf[0], buf[1], buf[2], buf[3], buf[4]);
+
+ /* Invalid code; 'Pile of poo' without the last byte. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x92, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 'A', &s)), (size_t)-1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno);
+ ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]);
+
+ /* Invalid code; 'Pile of poo' without the first byte. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), (size_t)-1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(errno, EILSEQ, "errno=%d", errno);
+ ATF_CHECK_EQ_MSG((unsigned char)buf[0], 0xcc, "buf=[%02x]", buf[0]);
+
+ /* Incomplete Unicode character 'Pile of poo', interrupted by NUL. */
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, '\0', &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, '\0', &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+
+ memset(&s, 0, sizeof(s));
+ memset(buf, 0xcc, sizeof(buf));
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0xf0, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x9f, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, 0x92, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = c8rtomb(buf, '\0', &s)), 1, "n=%zu", n);
+ ATF_CHECK_MSG(((unsigned char)buf[0] == '\0' &&
+ (unsigned char)buf[1] == 0xcc),
+ "buf=[%02x %02x]", buf[0], buf[1]);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, c8rtomb_c_locale_test);
+ ATF_TP_ADD_TC(tp, c8rtomb_iso2022jp_locale_test);
+ ATF_TP_ADD_TC(tp, c8rtomb_iso_8859_1_test);
+ ATF_TP_ADD_TC(tp, c8rtomb_iso_8859_15_test);
+ ATF_TP_ADD_TC(tp, c8rtomb_utf_8_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/locale/t_mbrtoc16.c b/lib/libc/locale/t_mbrtoc16.c
new file mode 100644
index 000000000000..cac804c84eed
--- /dev/null
+++ b/lib/libc/locale/t_mbrtoc16.c
@@ -0,0 +1,364 @@
+/* $NetBSD: t_mbrtoc16.c,v 1.3 2024/08/20 17:43:09 riastradh Exp $ */
+
+/*-
+ * 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 <sys/cdefs.h>
+__RCSID("$NetBSD: t_mbrtoc16.c,v 1.3 2024/08/20 17:43:09 riastradh Exp $");
+
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <locale.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_EQ_MSG(strcmp(lc_ctype_set, locale_name), 0,
+ "lc_ctype_set=%s locale_name=%s", lc_ctype_set, locale_name);
+}
+
+static mbstate_t s;
+static char16_t c16;
+
+ATF_TC_WITHOUT_HEAD(mbrtoc16_c_locale_test);
+ATF_TC_BODY(mbrtoc16_c_locale_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("C");
+
+ /* Null wide character, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 1, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%"PRIx16, (uint16_t)c16);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 1, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%"PRIx16, (uint16_t)c16);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(NULL, 0, 0, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A", 1, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%"PRIx16" L'A'=U+%"PRIx16,
+ (uint16_t)c16, (uint16_t)L'A');
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%"PRIx16" L'A'=U+%"PRIx16,
+ (uint16_t)c16, (uint16_t)L'A');
+
+ /* Incomplete character sequence. */
+ c16 = L'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'z', "c16=U+%"PRIx16" L'z'=U+%"PRIx16,
+ (uint16_t)c16, (uint16_t)L'z');
+
+ /* Check that mbrtoc16() doesn't access the buffer when n == 0. */
+ c16 = L'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'z', "c16=U+%"PRIx16" L'z'=U+%"PRIx16,
+ (uint16_t)c16, (uint16_t)L'z');
+
+ /* Check that mbrtoc16() doesn't read ahead too aggressively. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "AB", 2, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%"PRIx16" L'A'=U+%"PRIx16,
+ (uint16_t)c16, (uint16_t)L'A');
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "C", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'C', "c16=U+%"PRIx16" L'C'=U+%"PRIx16,
+ (uint16_t)c16, (uint16_t)L'C');
+}
+
+ATF_TC_WITHOUT_HEAD(mbrtoc16_iso2022jp_locale_test);
+ATF_TC_BODY(mbrtoc16_iso2022jp_locale_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("ja_JP.ISO-2022-JP");
+
+ /* Null wide character, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 1, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 1, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(NULL, 0, 0, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A", 1, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%04"PRIx16" L'A'=U+%04"PRIx16,
+ (uint16_t)c16, (uint16_t)L'A');
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%04"PRIx16" L'A'=U+%04"PRIx16,
+ (uint16_t)c16, (uint16_t)L'A');
+
+ /* Incomplete character sequence. */
+ c16 = L'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'z', "c16=U+%04"PRIx16" L'z'=U+%04"PRIx16,
+ (uint16_t)c16, (uint16_t)L'z');
+
+ /* Check that mbrtoc16() doesn't access the buffer when n == 0. */
+ c16 = L'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'z', "c16=U+%04"PRIx16" L'z'=U+%04"PRIx16,
+ (uint16_t)c16, (uint16_t)L'z');
+
+ /* Check that mbrtoc16() doesn't read ahead too aggressively. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "AB", 2, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%04"PRIx16" L'A'=U+%04"PRIx16,
+ (uint16_t)c16, (uint16_t)L'A');
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "C", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'C', "c16=U+%04"PRIx16" L'C'=U+%04"PRIx16,
+ (uint16_t)c16, (uint16_t)L'C');
+
+ /* Incomplete character sequence (shift sequence only). */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x1b(J", 3, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16);
+
+ /* Same as above, but complete (U+00A5 YEN SIGN). */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x1b(J\x5c", 4, &s)), 4,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0xa5, "c16=U+%04"PRIx16, (uint16_t)c16);
+
+ /* Test restarting behaviour. */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x1b(", 2, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "J\x5c", 2, &s)), 2, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0xa5, "c16=U+%04"PRIx16, (uint16_t)c16);
+
+ /*
+ * Test shift sequence state in various increments:
+ * 1. U+0042 LATIN CAPITAL LETTER A
+ * 2. (shift ISO/IEC 646:JP) U+00A5 YEN SIGN
+ * 3. U+00A5 YEN SIGN
+ * 4. (shift JIS X 0208) U+30A2 KATAKANA LETTER A
+ * 5. U+30A2 KATAKANA LETTER A
+ * 6. (shift to initial state) U+0000 NUL
+ */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A\x1b(J", 4, &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%04"PRIx16, (uint16_t)c16);
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x1b(J", 3, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x5c\x5c", 2, &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0x00a5, "c16=U+%04"PRIx16, (uint16_t)c16);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x5c\x1b$", 3, &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0x00a5, "c16=U+%04"PRIx16, (uint16_t)c16);
+ c16 = 0x1234;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x1b", 1, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0x1234, "c16=U+%04"PRIx16, (uint16_t)c16);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "$B\x25\x22", 4, &s)), 4,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0x30a2, "c16=U+%04"PRIx16, (uint16_t)c16);
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x25", 1, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x22\x1b(B\x00", 5, &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0x30a2, "c16=U+%04"PRIx16, (uint16_t)c16);
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x1b(", 2, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16);
+ c16 = 42;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "B\x00", 2, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%04"PRIx16, (uint16_t)c16);
+}
+
+ATF_TC_WITHOUT_HEAD(mbrtoc16_iso_8859_1_test);
+ATF_TC_BODY(mbrtoc16_iso_8859_1_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.ISO8859-1");
+
+ /* Currency sign. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xa4", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0xa4, "c16=U+%"PRIx16, (uint16_t)c16);
+}
+
+ATF_TC_WITHOUT_HEAD(mbrtoc16_iso_8859_15_test);
+ATF_TC_BODY(mbrtoc16_iso_8859_15_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.ISO8859-15");
+
+ /* Euro sign. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xa4", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0x20ac, "c16=U+%"PRIx16, (uint16_t)c16);
+}
+
+ATF_TC_WITHOUT_HEAD(mbrtoc16_utf_8_test);
+ATF_TC_BODY(mbrtoc16_utf_8_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.UTF-8");
+
+ /* Null wide character, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(NULL, 0, 0, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 1, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%"PRIx16, (uint16_t)c16);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 1, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%"PRIx16, (uint16_t)c16);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(NULL, 0, 0, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A", 1, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%"PRIx16" L'A'=U+%"PRIx16,
+ (uint16_t)c16, (uint16_t)L'A');
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "A", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'A', "c16=U+%"PRIx16" L'A'=U+%"PRIx16,
+ (uint16_t)c16, (uint16_t)L'A');
+
+ /* Incomplete character sequence (zero length). */
+ c16 = L'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, L'z', "c16=U+%"PRIx16" L'z'=U+%"PRIx16,
+ (uint16_t)c16, (uint16_t)L'z');
+
+ /* Incomplete character sequence (truncated double-byte). */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xc3", 1, &s)), (size_t)-2,
+ "n=%zu", n);
+
+ /* Same as above, but complete. */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xc3\x84", 2, &s)), 2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0xc4, "c16=U+%"PRIx16, (uint16_t)c16);
+
+ /* Test restarting behaviour. */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xc3", 1, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0, "c16=U+%"PRIx16, (uint16_t)c16);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xb7", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0xf7, "c16=U+%"PRIx16, (uint16_t)c16);
+
+ /* Surrogate pair. */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xf0\x9f\x92\xa9", 4, &s)), 4,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0xd83d, "c16=U+%"PRIx16, (uint16_t)c16);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0xdca9, "c16=U+%"PRIx16, (uint16_t)c16);
+
+ /* Letter e with acute, precomposed. */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xc3\xa9", 2, &s)), 2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0xe9, "c16=U+%"PRIx16, (uint16_t)c16);
+
+ /* Letter e with acute, combined. */
+ memset(&s, 0, sizeof(s));
+ c16 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\x65\xcc\x81", 3, &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0x65, "c16=U+%"PRIx16, (uint16_t)c16);
+ ATF_CHECK_EQ_MSG((n = mbrtoc16(&c16, "\xcc\x81", 2, &s)), 2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c16, 0x301, "c16=U+%"PRIx16, (uint16_t)c16);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mbrtoc16_c_locale_test);
+ ATF_TP_ADD_TC(tp, mbrtoc16_iso2022jp_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/locale/t_mbrtoc32.c b/lib/libc/locale/t_mbrtoc32.c
new file mode 100644
index 000000000000..8ccc421400a8
--- /dev/null
+++ b/lib/libc/locale/t_mbrtoc32.c
@@ -0,0 +1,61 @@
+/* $NetBSD: t_mbrtoc32.c,v 1.1 2024/08/15 14:16:34 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_mbrtoc32.c,v 1.1 2024/08/15 14:16:34 riastradh Exp $");
+
+#include <atf-c.h>
+#include <locale.h>
+#include <uchar.h>
+
+#include "h_macros.h"
+
+ATF_TC(mbrtoc32_null);
+ATF_TC_HEAD(mbrtoc32_null, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test null string input to mbrtoc32");
+}
+ATF_TC_BODY(mbrtoc32_null, tc)
+{
+ char *locale;
+ char32_t c32;
+ mbstate_t ps = {0};
+ size_t n;
+
+ REQUIRE_LIBC((locale = setlocale(LC_ALL, "C")), NULL);
+ ATF_REQUIRE_EQ_MSG(strcmp(locale, "C"), 0, "locale=%s", locale);
+
+ ATF_CHECK_EQ_MSG((n = mbrtoc32(&c32, NULL, 0, &ps)), 0, "n=%zu", n);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mbrtoc32_null);
+ return atf_no_error();
+}
diff --git a/lib/libc/locale/t_mbrtoc8.c b/lib/libc/locale/t_mbrtoc8.c
new file mode 100644
index 000000000000..760f19a62f08
--- /dev/null
+++ b/lib/libc/locale/t_mbrtoc8.c
@@ -0,0 +1,415 @@
+/* $NetBSD: t_mbrtoc8.c,v 1.3 2024/08/20 17:43:09 riastradh Exp $ */
+
+/*-
+ * 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 mbrtoc8() as specified by C23.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_mbrtoc8.c,v 1.3 2024/08/20 17:43:09 riastradh Exp $");
+
+#include <errno.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <locale.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_EQ_MSG(strcmp(lc_ctype_set, locale_name), 0,
+ "lc_ctype_set=%s locale_name=%s", lc_ctype_set, locale_name);
+}
+
+static mbstate_t s;
+static char8_t c8;
+
+ATF_TC_WITHOUT_HEAD(mbrtoc8_c_locale_test);
+ATF_TC_BODY(mbrtoc8_c_locale_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("C");
+
+ /* Null wide character, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 1, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 1, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(NULL, 0, 0, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A", 1, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'A');
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'A');
+
+ /* Incomplete character sequence. */
+ c8 = 'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'z', "c8=0x%"PRIx8" 'z'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'z');
+
+ /* Check that mbrtoc8() doesn't access the buffer when n == 0. */
+ c8 = 'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'z', "c8=0x%"PRIx8" 'z'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'z');
+
+ /* Check that mbrtoc8() doesn't read ahead too aggressively. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "AB", 2, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'A');
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "C", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'C', "c8=0x%"PRIx8" 'C'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'C');
+
+}
+
+ATF_TC_WITHOUT_HEAD(mbrtoc8_iso2022jp_locale_test);
+ATF_TC_BODY(mbrtoc8_iso2022jp_locale_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("ja_JP.ISO-2022-JP");
+
+ /* Null wide character, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 1, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 1, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(NULL, 0, 0, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A", 1, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'A');
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'A');
+
+ /* Incomplete character sequence. */
+ c8 = 'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'z', "c8=0x%"PRIx8" 'z'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'z');
+
+ /* Check that mbrtoc8() doesn't access the buffer when n == 0. */
+ c8 = 'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'z', "c8=0x%"PRIx8" 'z'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'z');
+
+ /* Check that mbrtoc8() doesn't read ahead too aggressively. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "AB", 2, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'A');
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "C", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'C', "c8=0x%"PRIx8" 'C'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'C');
+
+ /* Incomplete character sequence (shift sequence only). */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(J", 3, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Same as above, but complete (U+00A5 YEN SIGN). */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(J\x5c", 4, &s)), 4,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xc2, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xa5, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Test restarting behaviour. */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(", 2, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "J\x5c", 2, &s)), 2, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xc2, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xa5, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /*
+ * Test shift sequence state in various increments:
+ * 1. U+0042 LATIN CAPITAL LETTER A
+ * 2. (shift ISO/IEC 646:JP) U+00A5 YEN SIGN
+ * 3. U+00A5 YEN SIGN
+ * 4. (shift JIS X 0208) U+30A2 KATAKANA LETTER A
+ * 5. U+30A2 KATAKANA LETTER A
+ * 6. (shift to initial state) U+0000 NUL
+ */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A\x1b(J", 4, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8, (uint8_t)c8);
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(J", 3, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x5c\x5c", 2, &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xc2, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x5c\x1b$", 3, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xa5, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x5c\x1b$", 3, &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xc2, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b", 1, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xa5, "c8=0x%"PRIx8, (uint8_t)c8);
+ c8 = 0xff;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b", 1, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xff, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "$B\x25\x22", 4, &s)), 4,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xe3, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x25", 1, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0x82, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x25", 1, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xa2, "c8=0x%"PRIx8, (uint8_t)c8);
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x25", 1, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x22\x1b(B\x00", 5, &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xe3, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(", 2, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0x82, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(", 2, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xa2, "c8=0x%"PRIx8, (uint8_t)c8);
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x1b(", 2, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+ c8 = 42;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "B\x00", 2, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+}
+
+ATF_TC_WITHOUT_HEAD(mbrtoc8_iso_8859_1_test);
+ATF_TC_BODY(mbrtoc8_iso_8859_1_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.ISO8859-1");
+
+ /* Currency sign. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xa4", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xc2, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xa4, "c8=0x%"PRIx8, (uint8_t)c8);
+}
+
+ATF_TC_WITHOUT_HEAD(mbrtoc8_iso_8859_15_test);
+ATF_TC_BODY(mbrtoc8_iso_8859_15_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.ISO8859-15");
+
+ /* Euro sign. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xa4", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xe2, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0x82, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xac, "c8=0x%"PRIx8, (uint8_t)c8);
+}
+
+ATF_TC_WITHOUT_HEAD(mbrtoc8_utf_8_test);
+ATF_TC_BODY(mbrtoc8_utf_8_test, tc)
+{
+ size_t n;
+
+ require_lc_ctype("en_US.UTF-8");
+
+ /* Null wide character, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(NULL, 0, 0, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 1, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Null wide character. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 1, &s)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Latin letter A, internal state. */
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(NULL, 0, 0, NULL)), 0, "n=%zu", n);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A", 1, NULL)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'A');
+
+ /* Latin letter A. */
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "A", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'A', "c8=0x%"PRIx8" 'A'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'A');
+
+ /* Incomplete character sequence (zero length). */
+ c8 = 'z';
+ memset(&s, 0, sizeof(s));
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 'z', "c8=0x%"PRIx8" 'z'=0x%"PRIx8,
+ (uint8_t)c8, (uint8_t)'z');
+
+ /* Incomplete character sequence (truncated double-byte). */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xc3", 1, &s)), (size_t)-2,
+ "n=%zu", n);
+
+ /* Same as above, but complete. */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xc3\x84", 2, &s)), 2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xc3, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0x84, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Test restarting behaviour. */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xc3", 1, &s)), (size_t)-2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xb7", 1, &s)), 1, "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xc3, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xb7, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Four-byte sequence. */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xf0\x9f\x92\xa9", 4, &s)), 4,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xf0, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0x9f, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0x92, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xa9, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Letter e with acute, precomposed. */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xc3\xa9", 2, &s)), 2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xc3, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xa9, "c8=0x%"PRIx8, (uint8_t)c8);
+
+ /* Letter e with acute, combined. */
+ memset(&s, 0, sizeof(s));
+ c8 = 0;
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\x65\xcc\x81", 3, &s)), 1,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0x65, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "\xcc\x81", 2, &s)), 2,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0xcc, "c8=0x%"PRIx8, (uint8_t)c8);
+ ATF_CHECK_EQ_MSG((n = mbrtoc8(&c8, "", 0, &s)), (size_t)-3,
+ "n=%zu", n);
+ ATF_CHECK_EQ_MSG(c8, 0x81, "c8=0x%"PRIx8, (uint8_t)c8);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, mbrtoc8_c_locale_test);
+ ATF_TP_ADD_TC(tp, mbrtoc8_iso2022jp_locale_test);
+ ATF_TP_ADD_TC(tp, mbrtoc8_iso_8859_1_test);
+ ATF_TP_ADD_TC(tp, mbrtoc8_iso_8859_15_test);
+ ATF_TP_ADD_TC(tp, mbrtoc8_utf_8_test);
+
+ return (atf_no_error());
+}
diff --git a/lib/libc/locale/t_uchar.c b/lib/libc/locale/t_uchar.c
new file mode 100644
index 000000000000..66e4830fb88a
--- /dev/null
+++ b/lib/libc/locale/t_uchar.c
@@ -0,0 +1,81 @@
+/* $NetBSD: t_uchar.c,v 1.3 2024/10/14 06:02:14 rillig Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Include <uchar.h> first to verify it declares everything we need.
+ */
+#include <uchar.h>
+typedef mbstate_t nbtest_mbstate_t;
+typedef size_t nbtest_size_t;
+typedef char8_t nbtest_char8_t;
+typedef char16_t nbtest_char16_t;
+typedef char32_t nbtest_char32_t;
+static size_t (*nbtest_mbrtoc8)(char8_t *restrict, const char *restrict,
+ size_t, mbstate_t *restrict) __unused = &mbrtoc8;
+static size_t (*nbtest_c8rtomb)(char *restrict, char8_t,
+ mbstate_t *restrict) __unused = &c8rtomb;
+static size_t (*nbtest_mbrtoc16)(char16_t *restrict, const char *restrict,
+ size_t, mbstate_t *restrict) __unused = &mbrtoc16;
+static size_t (*nbtest_c16rtomb)(char *restrict, char16_t,
+ mbstate_t *restrict) __unused = &c16rtomb;
+static size_t (*nbtest_mbrtoc32)(char32_t *restrict, const char *restrict,
+ size_t, mbstate_t *restrict) __unused = mbrtoc32;
+static size_t (*nbtest_c32rtomb)(char *restrict, char32_t,
+ mbstate_t *restrict) __unused = &c32rtomb;
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_uchar.c,v 1.3 2024/10/14 06:02:14 rillig Exp $");
+
+#include <atf-c.h>
+#include <stdint.h>
+
+ATF_TC(uchartypes);
+ATF_TC_HEAD(uchartypes, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test <uchar.h> types are reasonable");
+}
+ATF_TC_BODY(uchartypes, tc)
+{
+
+ ATF_CHECK_EQ_MSG(sizeof(char8_t), sizeof(unsigned char),
+ "char8_t %zu, unsigned char %zu",
+ sizeof(char8_t), sizeof(unsigned char));
+ ATF_CHECK_EQ_MSG(sizeof(char16_t), sizeof(uint_least16_t),
+ "char16_t %zu, uint_least16_t %zu",
+ sizeof(char16_t), sizeof(uint_least16_t));
+ ATF_CHECK_EQ_MSG(sizeof(char32_t), sizeof(uint_least32_t),
+ "char32_t %zu, uint_least32_t %zu",
+ sizeof(char32_t), sizeof(uint_least32_t));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, uchartypes);
+ return atf_no_error();
+}
diff --git a/lib/libc/misc/t_vis.c b/lib/libc/misc/t_vis.c
new file mode 100644
index 000000000000..a712ffa2f9d2
--- /dev/null
+++ b/lib/libc/misc/t_vis.c
@@ -0,0 +1,165 @@
+/* $NetBSD: t_vis.c,v 1.2 2025/11/16 12:43:25 nia Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Nia Alarie.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <atf-c.h>
+#include <string.h>
+#include <stdio.h>
+#include "vis_types.h"
+#include "vis_proto.h"
+
+ATF_TC(vis_test_addsub);
+
+ATF_TC_HEAD(vis_test_addsub, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test 32-bit packed add/subtract");
+}
+
+ATF_TC_BODY(vis_test_addsub, tc)
+{
+ vis_d64 v1, v2, v3;
+ vis_f32 f1, f2;
+ vis_u32 u1, u2;
+
+ v1 = vis_to_double(8, 16);
+ v2 = vis_to_double(16, 8);
+
+ v3 = vis_fpadd32(v1, v2);
+
+ f1 = vis_read_lo(v3);
+ memcpy(&u1, &f1, sizeof(f1));
+ f2 = vis_read_hi(v3);
+ memcpy(&u2, &f2, sizeof(f2));
+
+ ATF_REQUIRE(u1 == 24 && u2 == 24);
+
+ v2 = vis_to_double(4, 4);
+ v3 = vis_fpsub32(v3, v2);
+
+ f1 = vis_read_lo(v3);
+ memcpy(&u1, &f1, sizeof(f1));
+ f2 = vis_read_hi(v3);
+ memcpy(&u2, &f2, sizeof(f2));
+
+ ATF_REQUIRE(u1 == 20 && u2 == 20);
+}
+
+ATF_TC(vis_test_bitwise);
+
+ATF_TC_HEAD(vis_test_bitwise, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test 32-bit packed bitwise");
+}
+
+ATF_TC_BODY(vis_test_bitwise, tc)
+{
+ static vis_u8 testbytes1[8] = { 1, 0, 1, 1, 1, 0, 1, 1 };
+ static vis_u8 testbytes2[8] = { 1, 1, 0, 1, 1, 1, 0, 1 };
+ static vis_u8 test_and[8] = { 1, 0, 0, 1, 1, 0, 0, 1 };
+ static vis_u8 test_or[8] = { 1, 1, 1, 1, 1, 1, 1, 1 };
+ static vis_u8 test_zero[8] = { 0 };
+ static vis_u64 test_ones = 0xffffffffffffffff;
+ double v1, v2, v3;
+
+ memcpy(&v1, testbytes1, sizeof(v1));
+ memcpy(&v2, testbytes2, sizeof(v2));
+
+ v3 = vis_fand(v1, v2);
+
+ ATF_REQUIRE(memcmp(&v3, test_and, sizeof(v3)) == 0);
+
+ v3 = vis_fone();
+
+ ATF_REQUIRE(memcmp(&v3, &test_ones, sizeof(v3)) == 0);
+
+ v3 = vis_for(v1, v2);
+
+ ATF_REQUIRE(memcmp(&v3, test_or, sizeof(v3)) == 0);
+
+ v3 = vis_fzero();
+
+ ATF_REQUIRE(memcmp(&v3, test_zero, sizeof(v3)) == 0);
+}
+
+ATF_TC(vis_test_fcmpeq16);
+
+ATF_TC_HEAD(vis_test_fcmpeq16, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test 16-bit packed compare");
+}
+
+ATF_TC_BODY(vis_test_fcmpeq16, tc)
+{
+ static vis_u16 testshort1[4] = { 16000, 16000, 16000, 16000 };
+ static vis_u16 testshort2[4] = { 32000, 16000, 32000, 16000 };
+ static vis_u16 testshort3[4] = { 48000, 48000, 48000, 48000 };
+ vis_d64 v1, v2, v3;
+
+ memcpy(&v1, testshort1, sizeof(v1));
+ memcpy(&v2, testshort2, sizeof(v2));
+ memcpy(&v3, testshort3, sizeof(v3));
+
+ ATF_REQUIRE((!!vis_fcmpeq16(v1, v2)) != 0);
+ ATF_REQUIRE((!!vis_fcmpeq16(v1, v3)) == 0);
+ ATF_REQUIRE((!!vis_fcmpne16(v1, v3)) != 0);
+}
+
+ATF_TC(vis_test_fcmpeq32);
+
+ATF_TC_HEAD(vis_test_fcmpeq32, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test 32-bit packed compare");
+}
+
+ATF_TC_BODY(vis_test_fcmpeq32, tc)
+{
+ static vis_u32 testlong1[2] = { 16000, 16000 };
+ static vis_u32 testlong2[2] = { 32000, 16000 };
+ static vis_u32 testlong3[2] = { 48000, 48000 };
+ vis_d64 v1, v2, v3;
+
+ memcpy(&v1, testlong1, sizeof(v1));
+ memcpy(&v2, testlong2, sizeof(v2));
+ memcpy(&v3, testlong3, sizeof(v3));
+
+ ATF_REQUIRE((!!vis_fcmpeq32(v1, v2)) != 0);
+ ATF_REQUIRE((!!vis_fcmpeq32(v1, v3)) == 0);
+ ATF_REQUIRE((!!vis_fcmpne32(v1, v3)) != 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, vis_test_addsub);
+ ATF_TP_ADD_TC(tp, vis_test_bitwise);
+ ATF_TP_ADD_TC(tp, vis_test_fcmpeq16);
+ ATF_TP_ADD_TC(tp, vis_test_fcmpeq32);
+
+ return atf_no_error();
+}
diff --git a/lib/libc/regex/t_regex_binary.c b/lib/libc/regex/t_regex_binary.c
new file mode 100644
index 000000000000..0c3d8cb9b908
--- /dev/null
+++ b/lib/libc/regex/t_regex_binary.c
@@ -0,0 +1,80 @@
+/* $NetBSD: t_regex_binary.c,v 1.1 2025/01/01 18:13:48 christos Exp $ */
+
+/*-
+ * Copyright (c) 2024 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: t_regex_binary.c,v 1.1 2025/01/01 18:13:48 christos Exp $");
+
+#include <atf-c.h>
+#include <regex.h>
+
+ATF_TC(negative_ranges);
+ATF_TC_HEAD(negative_ranges, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test negative ranges compilation");
+}
+ATF_TC_BODY(negative_ranges, tc)
+{
+ regex_t re;
+ char msg[1024];
+ int e;
+
+ if ((e = regcomp(&re, "[\xe0-\xf1][\xa0-\xd1].*", REG_EXTENDED)) != 0) {
+ regerror(e, &re, msg, sizeof(msg));
+ ATF_REQUIRE_MSG(0, "regcomp failed %s", msg);
+ }
+}
+
+ATF_TC(negative_char);
+ATF_TC_HEAD(negative_char, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test negative char in braces compilation");
+}
+ATF_TC_BODY(negative_char, tc)
+{
+ regex_t re;
+ char msg[1024];
+ int e;
+
+ /* PR/58910 */
+ if ((e = regcomp(&re, ": j:[]j:[]j:[\xd9j:[]", REG_EXTENDED)) != 0) {
+ regerror(e, &re, msg, sizeof(msg));
+ ATF_REQUIRE_MSG(0, "regcomp failed %s", msg);
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, negative_ranges);
+ ATF_TP_ADD_TC(tp, negative_char);
+ return atf_no_error();
+}
diff --git a/lib/libc/setjmp/t_sigstack.c b/lib/libc/setjmp/t_sigstack.c
new file mode 100644
index 000000000000..ea827da9545c
--- /dev/null
+++ b/lib/libc/setjmp/t_sigstack.c
@@ -0,0 +1,389 @@
+/* $NetBSD: t_sigstack.c,v 1.25 2025/05/12 14:46:19 christos Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_sigstack.c,v 1.25 2025/05/12 14:46:19 christos Exp $");
+
+#include <dlfcn.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <ucontext.h>
+
+#include "h_macros.h"
+
+struct sigaltstack ss[3];
+jmp_buf jmp;
+sigjmp_buf sigjmp;
+unsigned nentries;
+const char *bailname;
+void (*bailfn)(void) __dead;
+
+/*
+ * Optional compat13 functions from when sigcontext was expanded.
+ * Fortunately the only change visible to the caller is that the size
+ * of jmp_buf increased, so we can always use the old symbols with new
+ * jmp_buf arrays.
+ */
+int (*compat13_sigsetjmp)(sigjmp_buf, int);
+void (*compat13_siglongjmp)(sigjmp_buf, int) __dead;
+int (*compat13_setjmp)(jmp_buf);
+void (*compat13_longjmp)(jmp_buf, int) __dead;
+
+/*
+ * compatsigsys(signo)
+ *
+ * Signal handler for SIGSYS in case compat_13_sigreturn13 is not
+ * implemented by the kernel -- we will just skip the test in that
+ * case.
+ */
+static void
+compatsigsys(int signo)
+{
+
+ atf_tc_skip("no compat syscalls to test");
+}
+
+static void
+compatsetup(void)
+{
+
+ /*
+ * Grab the libc library symbols if available.
+ */
+ if ((compat13_sigsetjmp = dlsym(RTLD_SELF, "sigsetjmp")) == NULL ||
+ (compat13_siglongjmp = dlsym(RTLD_SELF, "siglongjmp")) == NULL ||
+ (compat13_setjmp = dlsym(RTLD_SELF, "setjmp")) == NULL ||
+ (compat13_longjmp = dlsym(RTLD_SELF, "longjmp")) == NULL)
+ atf_tc_skip("no compat functions to test");
+
+ /*
+ * Arrange for SIGSYS to skip the test -- this happens if the
+ * libc stub has the function, but the kernel isn't built with
+ * support for the compat13 sigreturn syscall for longjmp.
+ */
+ REQUIRE_LIBC(signal(SIGSYS, &compatsigsys), SIG_ERR);
+}
+
+static void
+on_sigusr1(int signo, siginfo_t *si, void *ctx)
+{
+ ucontext_t *uc = ctx;
+ void *sp = (void *)(uintptr_t)_UC_MACHINE_SP(uc);
+ void *fp = __builtin_frame_address(0);
+ struct sigaltstack *ssp;
+
+ /*
+ * Ensure we haven't re-entered the signal handler too many
+ * times. We should enter only twice.
+ */
+ ATF_REQUIRE_MSG(nentries < 2,
+ "%u recursive signal handler entries is too many in this test",
+ nentries + 1);
+
+ /*
+ * Ensure that the signal handler was called in the alternate
+ * signal stack.
+ */
+ ssp = &ss[nentries];
+ ATF_REQUIRE_MSG((fp >= ssp->ss_sp &&
+ fp < (void *)((char *)ssp->ss_sp + ssp->ss_size)),
+ "sigaltstack failed to take effect on entry %u --"
+ " signal handler's frame pointer %p doesn't lie in sigaltstack"
+ " [%p, %p), size 0x%zx",
+ nentries,
+ fp, ssp->ss_sp, (char *)ssp->ss_sp + ssp->ss_size, ssp->ss_size);
+
+ /*
+ * Ensure that if we enter the signal handler, we are entering
+ * it from the original stack, not from any of the alternate
+ * signal stacks.
+ */
+ for (ssp = &ss[0]; ssp < &ss[__arraycount(ss)]; ssp++) {
+ ATF_REQUIRE_MSG((sp < ssp->ss_sp ||
+ sp >= (void *)((char *)ssp->ss_sp + ssp->ss_size)),
+ "%s failed to restore stack"
+ " before allowing signal on entry %u --"
+ " interrupted stack pointer %p lies in sigaltstack %zd"
+ " [%p, %p), size 0x%zx",
+ bailname,
+ nentries,
+ sp, ssp - ss,
+ ssp->ss_sp, (char *)ssp->ss_sp + ssp->ss_size,
+ ssp->ss_size);
+ }
+
+ /*
+ * First time through, we want to test whether longjmp restores
+ * the signal mask first, or restores the stack pointer first.
+ * The signal should be blocked at this point, so we re-raise
+ * the signal to queue it up for delivery as soon as it is
+ * unmasked -- which should wait until the stack pointer has
+ * been restored in longjmp.
+ */
+ if (nentries++ == 0)
+ RL(raise(SIGUSR1));
+
+ /*
+ * Set up the next sigaltstack. We can't reuse the current one
+ * for the next signal handler re-entry until the system clears
+ * the SS_ONSTACK process state -- which normal return from
+ * signal handler does, but which longjmp does not. So to keep
+ * it simple (ha), we just use another sigaltstack.
+ */
+ RL(sigaltstack(&ss[nentries], NULL));
+
+ /*
+ * Jump back to the original context.
+ */
+ (*bailfn)();
+}
+
+static void
+go(const char *name, void (*fn)(void) __dead)
+{
+ struct sigaction sa;
+ unsigned i;
+
+ bailname = name;
+ bailfn = fn;
+
+ /*
+ * Allocate a stack for the signal handler to run in, and
+ * configure the system to use the first one.
+ *
+ * XXX Should maybe use a guard page but this is simpler.
+ */
+ for (i = 0; i < __arraycount(ss); i++) {
+ ss[i].ss_size = SIGSTKSZ;
+ REQUIRE_LIBC(ss[i].ss_sp = malloc(ss[i].ss_size), NULL);
+ }
+ RL(sigaltstack(&ss[0], NULL));
+
+ /*
+ * Set up a test signal handler for SIGUSR1. Allow all
+ * signals, except SIGUSR1 (which is masked by default) -- that
+ * way we don't inadvertently obscure weird crashes in the
+ * signal handler.
+ *
+ * Set SA_SIGINFO so the system will pass siginfo -- and, more
+ * to the point, ucontext, so the signal handler can determine
+ * the stack pointer of the logic it interrupted.
+ *
+ * Set SA_ONSTACK so the system will use the alternate signal
+ * stack to call the signal handler -- that way, it can tell
+ * whether the stack was restored before the second time
+ * around.
+ */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_sigaction = &on_sigusr1;
+ RL(sigemptyset(&sa.sa_mask));
+ sa.sa_flags = SA_SIGINFO|SA_ONSTACK;
+ RL(sigaction(SIGUSR1, &sa, NULL));
+
+ /*
+ * Raise the signal to enter the signal handler the first time.
+ */
+ RL(raise(SIGUSR1));
+
+ /*
+ * If we ever reach this point, something went seriously wrong.
+ */
+ atf_tc_fail("unreachable");
+}
+
+static void __dead
+bail_longjmp(void)
+{
+
+ longjmp(jmp, 1);
+}
+
+ATF_TC(setjmp);
+ATF_TC_HEAD(setjmp, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test longjmp restores stack first, then signal mask");
+}
+ATF_TC_BODY(setjmp, tc)
+{
+
+#if defined __ia64__
+ atf_tc_expect_fail("PR lib/57946:"
+ " longjmp fails to restore stack first before"
+ " restoring signal mask on most architectures");
+#endif
+
+ /*
+ * Set up a return point for the signal handler: when the
+ * signal handler does longjmp(jmp, 1), it comes flying out of
+ * here.
+ */
+ if (setjmp(jmp) == 1)
+ return;
+
+ /*
+ * Run the test with longjmp.
+ */
+ go("longjmp", &bail_longjmp);
+}
+
+static void __dead
+bail_compat13_longjmp(void)
+{
+
+ (*compat13_longjmp)(jmp, 1);
+}
+
+ATF_TC(compat13_setjmp);
+ATF_TC_HEAD(compat13_setjmp, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test compat13 longjmp restores stack first, then signal mask");
+}
+ATF_TC_BODY(compat13_setjmp, tc)
+{
+
+ compatsetup();
+
+#if defined __arm__ || defined __i386__ || defined __sh3__
+#ifndef __arm__ /* will be exposed once PR 59351 is fixed */
+ atf_tc_expect_fail("PR lib/57946:"
+ " longjmp fails to restore stack first before"
+ " restoring signal mask on most architectures");
+#endif
+#endif
+#ifdef __arm__
+ atf_tc_expect_signal(-1, "PR port-arm/59351: compat_setjmp is busted");
+#endif
+
+ /*
+ * Set up a return point for the signal handler: when the
+ * signal handler does (*compat13_longjmp)(jmp, 1), it comes
+ * flying out of here.
+ */
+ if ((*compat13_setjmp)(jmp) == 1)
+ return;
+
+ /*
+ * Run the test with compat13_longjmp.
+ */
+ go("longjmp", &bail_compat13_longjmp);
+}
+
+static void __dead
+bail_siglongjmp(void)
+{
+
+ siglongjmp(sigjmp, 1);
+}
+
+ATF_TC(sigsetjmp);
+ATF_TC_HEAD(sigsetjmp, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test siglongjmp restores stack first, then signal mask");
+}
+ATF_TC_BODY(sigsetjmp, tc)
+{
+
+#if defined __ia64__
+ atf_tc_expect_fail("PR lib/57946:"
+ " longjmp fails to restore stack first before"
+ " restoring signal mask on most architectures");
+#endif
+
+ /*
+ * Set up a return point for the signal handler: when the
+ * signal handler does siglongjmp(sigjmp, 1), it comes flying
+ * out of here.
+ */
+ if (sigsetjmp(sigjmp, /*savesigmask*/1) == 1)
+ return;
+
+ /*
+ * Run the test with siglongjmp.
+ */
+ go("siglongjmp", &bail_siglongjmp);
+}
+
+static void __dead
+bail_compat13_siglongjmp(void)
+{
+
+ (*compat13_siglongjmp)(sigjmp, 1);
+}
+
+ATF_TC(compat13_sigsetjmp);
+ATF_TC_HEAD(compat13_sigsetjmp, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test compat13 siglongjmp restores stack first,"
+ " then signal mask");
+}
+ATF_TC_BODY(compat13_sigsetjmp, tc)
+{
+
+ compatsetup();
+
+#if defined __arm__ || defined __i386__ || defined __sh3__
+#ifndef __arm__ /* will be exposed once PR 59351 is fixed */
+ atf_tc_expect_fail("PR lib/57946:"
+ " longjmp fails to restore stack first before"
+ " restoring signal mask on most architectures");
+#endif
+#endif
+#ifdef __arm__
+ atf_tc_expect_signal(-1, "PR port-arm/59351: compat_setjmp is busted");
+#endif
+
+ /*
+ * Set up a return point for the signal handler: when the
+ * signal handler does (*compat13_siglongjmp)(sigjmp, 1), it
+ * comes flying out of here.
+ */
+ if ((*compat13_sigsetjmp)(sigjmp, /*savesigmask*/1) == 1)
+ return;
+
+ /*
+ * Run the test with compat13_siglongjmp.
+ */
+ go("siglongjmp", &bail_compat13_siglongjmp);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, compat13_setjmp);
+ ATF_TP_ADD_TC(tp, compat13_sigsetjmp);
+ ATF_TP_ADD_TC(tp, setjmp);
+ ATF_TP_ADD_TC(tp, sigsetjmp);
+
+ return atf_no_error();
+}
diff --git a/lib/libc/ssp/h_getcwd2.c b/lib/libc/ssp/h_getcwd2.c
new file mode 100644
index 000000000000..86cfd09f2bb0
--- /dev/null
+++ b/lib/libc/ssp/h_getcwd2.c
@@ -0,0 +1,18 @@
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+
+char*
+getcwd(char* buf, size_t buflen)
+{
+ errno = ENOSYS;
+ return NULL;
+}
+int
+main(void)
+{
+ char buf[256];
+ if (getcwd(buf, sizeof buf) == NULL)
+ err(1, "getcwd failed");
+ return 0;
+}
diff --git a/lib/libc/stdlib/h_sort.c b/lib/libc/stdlib/h_sort.c
new file mode 100644
index 000000000000..1e5f4b3b0798
--- /dev/null
+++ b/lib/libc/stdlib/h_sort.c
@@ -0,0 +1,225 @@
+/* $NetBSD: h_sort.c,v 1.3 2025/03/02 23:11:19 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: h_sort.c,v 1.3 2025/03/02 23:11:19 riastradh Exp $");
+
+#include <assert.h>
+#include <err.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void
+heapsort_r_wrapper(void *a, size_t n, size_t sz,
+ int (*cmp)(const void *, const void *, void *), void *cookie)
+{
+
+ if (heapsort_r(a, n, sz, cmp, cookie) == -1)
+ err(1, "heapsort_r");
+}
+
+static void
+mergesort_r_wrapper(void *a, size_t n, size_t sz,
+ int (*cmp)(const void *, const void *, void *), void *cookie)
+{
+
+ if (mergesort_r(a, n, sz, cmp, cookie) == -1)
+ err(1, "mergesort_r");
+}
+
+struct context {
+ const char *buf;
+ const size_t *linepos;
+};
+
+static int
+cmp(const void *va, const void *vb, void *cookie)
+{
+ const struct context *C = cookie;
+ const size_t *a = va;
+ const size_t *b = vb;
+
+ return strcmp(C->buf + C->linepos[*a], C->buf + C->linepos[*b]);
+}
+
+static void __dead
+usage(void)
+{
+
+ fprintf(stderr, "Usage: %s [-n] <sortfn>\n", getprogname());
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ int nflag = 0;
+ void (*sortfn)(void *, size_t, size_t,
+ int (*)(const void *, const void *, void *), void *);
+ char *buf = NULL;
+ size_t nbuf;
+ size_t *linepos = NULL;
+ size_t nlines;
+ size_t *permutation = NULL;
+ size_t off;
+ ssize_t nread;
+ char *p;
+ size_t i;
+ int error;
+
+ /*
+ * Parse arguments.
+ */
+ setprogname(argv[0]);
+ while ((ch = getopt(argc, argv, "hn")) != -1) {
+ switch (ch) {
+ case 'n':
+ nflag = 1;
+ break;
+ case '?':
+ case 'h':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 1)
+ usage();
+ if (strcmp(argv[0], "heapsort_r") == 0)
+ sortfn = &heapsort_r_wrapper;
+ else if (strcmp(argv[0], "mergesort_r") == 0)
+ sortfn = &mergesort_r_wrapper;
+ else if (strcmp(argv[0], "qsort_r") == 0)
+ sortfn = &qsort_r;
+ else
+ errx(1, "unknown sort: %s", argv[0]);
+
+ /*
+ * Allocate an initial 4K buffer.
+ */
+ nbuf = 0x1000;
+ error = reallocarr(&buf, nbuf, 1);
+ if (error)
+ errc(1, error, "reallocarr");
+
+ /*
+ * Read the input into a contiguous buffer. Reject input with
+ * embedded NULs so we can use strcmp(3) to compare lines.
+ */
+ off = 0;
+ while ((nread = read(STDIN_FILENO, buf + off, nbuf - off - 1)) != 0) {
+ if (nread == -1)
+ err(1, "read");
+ if ((size_t)nread > nbuf - off - 1)
+ errx(1, "overlong read: %zu", (size_t)nread);
+ if (memchr(buf + off, '\0', (size_t)nread) != NULL)
+ errx(1, "NUL byte in input");
+ off += (size_t)nread;
+
+ /*
+ * If we filled the buffer, reallocate it with double
+ * the size. Bail if that would overflow.
+ */
+ if (nbuf - off == 1) {
+ if (nbuf > SIZE_MAX/2)
+ errx(1, "input overflow");
+ nbuf *= 2;
+ error = reallocarr(&buf, nbuf, 1);
+ if (error)
+ errc(1, error, "reallocarr");
+ }
+ }
+
+ /*
+ * If the input was empty, nothing to do.
+ */
+ if (off == 0)
+ return 0;
+
+ /*
+ * NUL-terminate the input and count the lines. The last line
+ * may have no trailing \n.
+ */
+ buf[off] = '\0';
+ nlines = 1;
+ for (p = buf; (p = strchr(p, '\n')) != NULL;) {
+ if (*++p == '\0')
+ break;
+ nlines++;
+ }
+
+ /*
+ * Create an array of line positions to sort. NUL-terminate
+ * each line so we can use strcmp(3).
+ */
+ error = reallocarr(&linepos, nlines, sizeof(linepos[0]));
+ if (error)
+ errc(1, error, "reallocarr");
+ i = 0;
+ for (p = buf; linepos[i++] = p - buf, (p = strchr(p, '\n')) != NULL;) {
+ *p = '\0';
+ if (*++p == '\0')
+ break;
+ }
+ assert(i == nlines);
+
+ /*
+ * Create an array of permuted line numbers.
+ */
+ error = reallocarr(&permutation, nlines, sizeof(permutation[0]));
+ if (error)
+ errc(1, error, "reallocarr");
+ for (i = 0; i < nlines; i++)
+ permutation[i] = i;
+
+ /*
+ * Sort the lines via comparison function that consults the
+ * buffer as a cookie.
+ */
+ (*sortfn)(permutation, nlines, sizeof(permutation[0]), &cmp,
+ &(struct context) { .buf = buf, .linepos = linepos });
+
+ /*
+ * Print the lines in sorted order with the original line
+ * numbers.
+ */
+ for (i = 0; i < nlines; i++) {
+ const size_t j = permutation[i];
+ if (nflag)
+ printf("%zu %s\n", j, buf + linepos[j]);
+ else
+ printf("%s\n", buf + linepos[j]);
+ }
+ fflush(stdout);
+ return ferror(stdout);
+}
diff --git a/lib/libc/stdlib/t_sort.sh b/lib/libc/stdlib/t_sort.sh
new file mode 100644
index 000000000000..8987ac1ec623
--- /dev/null
+++ b/lib/libc/stdlib/t_sort.sh
@@ -0,0 +1,115 @@
+# $NetBSD: t_sort.sh,v 1.2 2025/03/02 20:00:32 riastradh Exp $
+#
+# Copyright (c) 2025 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+check_sort()
+{
+ local sortfn
+
+ set -eu
+
+ sortfn="$1"
+
+ printf 'foo\nbar\nbaz\nquux' >in1
+ printf '1 bar\n2 baz\n0 foo\n3 quux\n' >out1
+ atf_check -s exit:0 -o file:out1 \
+ "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in1
+
+ atf_check -s exit:0 -o empty sh -c 'exec shuffle -f - <in1 >in2'
+ printf 'bar\nbaz\nfoo\nquux\n' >out2
+ atf_check -s exit:0 -o file:out2 \
+ "$(atf_get_srcdir)"/h_sort "$sortfn" <in2
+}
+
+check_stablesort()
+{
+ local sortfn
+
+ set -eu
+
+ sortfn="$1"
+
+ printf 'foo\nfoo\nfoo\nfoo\nfoo' >in1
+ printf '0 foo\n1 foo\n2 foo\n3 foo\n4 foo\n' >out1
+ atf_check -s exit:0 -o file:out1 \
+ "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in1
+
+ printf 'foo\nfoo\nfoo\nfoo\nfoo\nbar\nbar\nbar\nbar\nbar' >in2
+ printf '5 bar\n6 bar\n7 bar\n8 bar\n9 bar\n' >out2
+ printf '0 foo\n1 foo\n2 foo\n3 foo\n4 foo\n' >>out2
+ atf_check -s exit:0 -o file:out2 \
+ "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in2
+
+ printf 'foo\nfoo\nbar\nbaz\nquux' >in3
+ printf '2 bar\n3 baz\n0 foo\n1 foo\n4 quux\n' >out3
+ atf_check -s exit:0 -o file:out3 \
+ "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in3
+
+ printf 'foo\nbar\nbar\nbaz\nquux' >in4
+ printf '1 bar\n2 bar\n3 baz\n0 foo\n4 quux\n' >out4
+ atf_check -s exit:0 -o file:out4 \
+ "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in4
+
+ printf 'foo\nbar\nbaz\nbaz\nquux' >in5
+ printf '1 bar\n2 baz\n3 baz\n0 foo\n4 quux\n' >out5
+ atf_check -s exit:0 -o file:out5 \
+ "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in5
+
+ printf 'foo\nbar\nbaz\nquux\nquux' >in6
+ printf '1 bar\n2 baz\n0 foo\n3 quux\n4 quux\n' >out6
+ atf_check -s exit:0 -o file:out6 \
+ "$(atf_get_srcdir)"/h_sort -n "$sortfn" <in6
+}
+
+sortfn_case()
+{
+ local sortfn
+
+ sortfn="$1"
+
+ eval "${sortfn}_head() { atf_set descr \"Test ${sortfn}\"; }"
+ eval "${sortfn}_body() { check_sort $sortfn; }"
+ atf_add_test_case "$sortfn"
+}
+
+stablesortfn_case()
+{
+ local sortfn
+
+ sortfn="$1"
+
+ eval "${sortfn}_stable_head() { atf_set descr \"Test ${sortfn}\"; }"
+ eval "${sortfn}_stable_body() { check_stablesort $sortfn; }"
+ atf_add_test_case "${sortfn}_stable"
+}
+
+atf_init_test_cases()
+{
+
+ sortfn_case heapsort_r
+ sortfn_case mergesort_r
+ sortfn_case qsort_r
+ stablesortfn_case mergesort_r
+}
diff --git a/lib/libc/sys/t_aio_cancel.c b/lib/libc/sys/t_aio_cancel.c
new file mode 100644
index 000000000000..43965cada251
--- /dev/null
+++ b/lib/libc/sys/t_aio_cancel.c
@@ -0,0 +1,223 @@
+/* $NetBSD: t_aio_cancel.c,v 1.1 2025/10/10 15:53:55 christos Exp $ */
+
+/*
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <atf-c.h>
+
+#include <aio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+static int mktemp_file(char *, size_t);
+static void fill_pattern(uint8_t *, size_t, uint8_t);
+static void wait_all(const struct aiocb * const [], size_t);
+
+static int
+mktemp_file(char *path, size_t pathlen)
+{
+ int fd, n;
+
+ n = snprintf(path, pathlen, "t_aio_cancel.XXXXXX");
+ ATF_REQUIRE(n > 0 && (size_t)n < pathlen);
+
+ fd = mkstemp(path);
+ ATF_REQUIRE(fd >= 0);
+
+ return fd;
+}
+
+static void
+fill_pattern(uint8_t *buf, size_t len, uint8_t seed)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ buf[i] = (uint8_t)(seed + (i & 0xff));
+ }
+}
+
+static void
+wait_all(const struct aiocb * const list[], size_t nent)
+{
+ size_t i;
+ int pending, rv;
+
+ for (;;) {
+ pending = 0;
+
+ for (i = 0; i < nent; i++) {
+ int err;
+
+ if (list[i] == NULL) {
+ continue;
+ }
+
+ err = aio_error(list[i]);
+ if (err == EINPROGRESS) {
+ pending = 1;
+ }
+ }
+
+ if (!pending) {
+ break;
+ }
+
+ rv = aio_suspend(list, (int)nent, NULL);
+ ATF_REQUIRE_EQ_MSG(0, rv, "aio_suspend failed: %s",
+ strerror(errno));
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(cancel_active_write);
+ATF_TC_BODY(cancel_active_write, tc)
+{
+ char path[64];
+ int fd, rv, crv, err;
+ const size_t blksz = 0x1000;
+ uint8_t *wbuf;
+ struct aiocb cb;
+ const struct aiocb *list[1];
+
+ fd = mktemp_file(path, sizeof(path));
+
+ wbuf = malloc(blksz);
+ ATF_REQUIRE(wbuf != NULL);
+ fill_pattern(wbuf, blksz, 0x33);
+
+ memset(&cb, 0, sizeof(cb));
+ cb.aio_fildes = fd;
+ cb.aio_buf = wbuf;
+ cb.aio_nbytes = blksz;
+ cb.aio_offset = 0;
+
+ rv = aio_write(&cb);
+ ATF_REQUIRE_EQ(0, rv);
+
+ crv = aio_cancel(fd, &cb);
+ ATF_REQUIRE(crv == AIO_CANCELED || crv == AIO_NOTCANCELED
+ || crv == AIO_ALLDONE);
+
+ if (crv == AIO_CANCELED) {
+ do {
+ err = aio_error(&cb);
+ } while (err == EINPROGRESS);
+ ATF_REQUIRE_EQ(ECANCELED, err);
+ ATF_REQUIRE_EQ(-1, aio_return(&cb));
+ } else if (crv == AIO_NOTCANCELED) {
+ list[0] = &cb;
+ wait_all(list, 1);
+ ATF_REQUIRE_EQ(0, aio_error(&cb));
+ ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&cb));
+ } else {
+ do {
+ err = aio_error(&cb);
+ } while (err == EINPROGRESS);
+ ATF_REQUIRE_EQ(0, err);
+ ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&cb));
+ }
+
+ rv = close(fd);
+ ATF_REQUIRE_EQ(0, rv);
+ rv = unlink(path);
+ ATF_REQUIRE_EQ(0, rv);
+
+ free(wbuf);
+}
+
+ATF_TC_WITHOUT_HEAD(cancel_completed_request);
+ATF_TC_BODY(cancel_completed_request, tc)
+{
+ char path[64];
+ int fd, rv, crv;
+ const size_t blksz = 4096;
+ uint8_t *wbuf;
+ struct aiocb cb;
+ const struct aiocb *list[1];
+
+ fd = mktemp_file(path, sizeof(path));
+
+ wbuf = malloc(blksz);
+ ATF_REQUIRE(wbuf != NULL);
+ memset(wbuf, 0x7E, blksz);
+
+ memset(&cb, 0, sizeof(cb));
+ cb.aio_fildes = fd;
+ cb.aio_buf = wbuf;
+ cb.aio_nbytes = blksz;
+ cb.aio_offset = 0;
+
+ rv = aio_write(&cb);
+ ATF_REQUIRE_EQ(0, rv);
+
+ list[0] = &cb;
+ wait_all(list, 1);
+ ATF_REQUIRE_EQ(0, aio_error(&cb));
+ ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&cb));
+
+ crv = aio_cancel(fd, &cb);
+ ATF_REQUIRE_EQ(AIO_ALLDONE, crv);
+
+ rv = close(fd);
+ ATF_REQUIRE_EQ(0, rv);
+ rv = unlink(path);
+ ATF_REQUIRE_EQ(0, rv);
+
+ free(wbuf);
+}
+
+ATF_TC_WITHOUT_HEAD(cancel_invalid_fd);
+ATF_TC_BODY(cancel_invalid_fd, tc)
+{
+ struct aiocb cb;
+ int crv;
+
+ memset(&cb, 0, sizeof(cb));
+ cb.aio_fildes = -1;
+
+ errno = 0;
+ crv = aio_cancel(-1, &cb);
+ ATF_REQUIRE_EQ(-1, crv);
+ ATF_REQUIRE_EQ(EBADF, errno);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, cancel_active_write);
+ ATF_TP_ADD_TC(tp, cancel_completed_request);
+ ATF_TP_ADD_TC(tp, cancel_invalid_fd);
+ return atf_no_error();
+}
diff --git a/lib/libc/sys/t_aio_lio.c b/lib/libc/sys/t_aio_lio.c
new file mode 100644
index 000000000000..991db8d5a7cd
--- /dev/null
+++ b/lib/libc/sys/t_aio_lio.c
@@ -0,0 +1,264 @@
+/* $NetBSD: t_aio_lio.c,v 1.1 2025/10/10 15:53:55 christos Exp $ */
+
+/*
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <atf-c.h>
+
+#include <aio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static int mktemp_file(char *, size_t);
+static void fill_pattern(uint8_t *, size_t, uint8_t);
+static void wait_all(const struct aiocb * const [], size_t);
+
+static int
+mktemp_file(char *path, size_t pathlen)
+{
+ int fd, n;
+
+ n = snprintf(path, pathlen, "t_aio_lio.XXXXXX");
+ ATF_REQUIRE(n > 0 && (size_t)n < pathlen);
+
+ fd = mkstemp(path);
+ ATF_REQUIRE(fd >= 0);
+
+ return fd;
+}
+
+static void
+fill_pattern(uint8_t *buf, size_t len, uint8_t seed)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ buf[i] = (uint8_t)(seed + (i & 0xff));
+ }
+}
+
+static void
+wait_all(const struct aiocb * const list[], size_t nent)
+{
+ size_t i;
+ int pending, rv;
+
+ for (;;) {
+ pending = 0;
+
+ for (i = 0; i < nent; i++) {
+ int err;
+
+ if (list[i] == NULL) {
+ continue;
+ }
+
+ err = aio_error(list[i]);
+ if (err == EINPROGRESS) {
+ pending = 1;
+ }
+ }
+
+ if (!pending) {
+ break;
+ }
+
+ rv = aio_suspend(list, (int)nent, NULL);
+ ATF_REQUIRE_EQ_MSG(0, rv, "aio_suspend failed: %s",
+ strerror(errno));
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(lio_nowait);
+ATF_TC_BODY(lio_nowait, tc)
+{
+ char path[64];
+ int fd, rv;
+#define NW_REQS 8
+#define NW_BLKSIZ 8192
+ uint8_t *bufs[NW_REQS];
+ struct aiocb cbs[NW_REQS];
+ struct aiocb *list[NW_REQS];
+ off_t off;
+ size_t i;
+
+ fd = mktemp_file(path, sizeof(path));
+
+ off = 0;
+ for (i = 0; i < NW_REQS; i++) {
+ bufs[i] = malloc(NW_BLKSIZ);
+ ATF_REQUIRE(bufs[i] != NULL);
+
+ fill_pattern(bufs[i], NW_BLKSIZ, (uint8_t)i);
+
+ memset(&cbs[i], 0, sizeof(cbs[i]));
+ cbs[i].aio_fildes = fd;
+ cbs[i].aio_buf = bufs[i];
+ cbs[i].aio_nbytes = NW_BLKSIZ;
+ cbs[i].aio_offset = off;
+ cbs[i].aio_lio_opcode = LIO_WRITE;
+
+ list[i] = &cbs[i];
+ off += (off_t)NW_BLKSIZ;
+ }
+
+ rv = lio_listio(LIO_NOWAIT, list, (int)NW_REQS, NULL);
+ ATF_REQUIRE_EQ_MSG(0, rv, "lio_listio failed: %s",
+ strerror(errno));
+
+ wait_all((const struct aiocb * const *)list, NW_REQS);
+
+ for (i = 0; i < NW_REQS; i++) {
+ int err;
+ ssize_t done;
+
+ err = aio_error(&cbs[i]);
+ ATF_REQUIRE_EQ(0, err);
+
+ done = aio_return(&cbs[i]);
+ ATF_REQUIRE_EQ(NW_BLKSIZ, done);
+
+ free(bufs[i]);
+ }
+
+ rv = close(fd);
+ ATF_REQUIRE_EQ(0, rv);
+ rv = unlink(path);
+ ATF_REQUIRE_EQ(0, rv);
+}
+
+ATF_TC_WITHOUT_HEAD(lio_wait_write_then_read);
+ATF_TC_BODY(lio_wait_write_then_read, tc)
+{
+ char path[64];
+ int fd, rv;
+#define WWTR_REQS 4
+#define WWTR_BLKSIZ 4096
+
+ uint8_t *wbufs[WWTR_REQS];
+ struct aiocb wcbs[WWTR_REQS];
+ struct aiocb *wlist[WWTR_REQS];
+
+ uint8_t *rbufs[WWTR_REQS];
+ struct aiocb rcbs[WWTR_REQS];
+ struct aiocb *rlist[WWTR_REQS];
+
+ size_t i;
+ off_t off;
+
+ fd = mktemp_file(path, sizeof(path));
+
+ off = 0;
+ for (i = 0; i < WWTR_REQS; i++) {
+ wbufs[i] = malloc(WWTR_BLKSIZ);
+ ATF_REQUIRE(wbufs[i] != NULL);
+
+ fill_pattern(wbufs[i], WWTR_BLKSIZ, (uint8_t)(0xA0 + i));
+
+ memset(&wcbs[i], 0, sizeof(wcbs[i]));
+ wcbs[i].aio_fildes = fd;
+ wcbs[i].aio_buf = wbufs[i];
+ wcbs[i].aio_nbytes = WWTR_BLKSIZ;
+ wcbs[i].aio_offset = off;
+ wcbs[i].aio_lio_opcode = LIO_WRITE;
+
+ wlist[i] = &wcbs[i];
+ off += WWTR_BLKSIZ;
+ }
+
+ rv = lio_listio(LIO_WAIT, wlist, (int)WWTR_REQS, NULL);
+ ATF_REQUIRE_EQ_MSG(0, rv, "lio_listio write failed: %s",
+ strerror(errno));
+
+ for (i = 0; i < WWTR_REQS; i++) {
+ int err;
+ ssize_t done;
+
+ err = aio_error(&wcbs[i]);
+ ATF_REQUIRE_EQ(0, err);
+
+ done = aio_return(&wcbs[i]);
+ ATF_REQUIRE_EQ(WWTR_BLKSIZ, done);
+ }
+
+ for (i = 0; i < WWTR_REQS; i++) {
+ rbufs[i] = calloc(1, WWTR_BLKSIZ);
+ ATF_REQUIRE(rbufs[i] != NULL);
+
+ memset(&rcbs[i], 0, sizeof(rcbs[i]));
+ rcbs[i].aio_fildes = fd;
+ rcbs[i].aio_buf = rbufs[i];
+ rcbs[i].aio_nbytes = WWTR_BLKSIZ;
+ rcbs[i].aio_offset = (off_t)i * WWTR_BLKSIZ;
+ rcbs[i].aio_lio_opcode = LIO_READ;
+
+ rlist[i] = &rcbs[i];
+ }
+
+ rv = lio_listio(LIO_NOWAIT, rlist, WWTR_REQS, NULL);
+ ATF_REQUIRE_EQ_MSG(0, rv, "lio_listio read failed: %s",
+ strerror(errno));
+
+ wait_all((const struct aiocb * const *)rlist, WWTR_REQS);
+
+ for (i = 0; i < WWTR_REQS; i++) {
+ int err;
+ ssize_t done;
+
+ err = aio_error(&rcbs[i]);
+ ATF_REQUIRE_EQ(0, err);
+
+ done = aio_return(&rcbs[i]);
+ ATF_REQUIRE_EQ(WWTR_BLKSIZ, done);
+
+ ATF_REQUIRE_EQ(0, memcmp(wbufs[i], rbufs[i], WWTR_BLKSIZ));
+
+ free(wbufs[i]);
+ free(rbufs[i]);
+ }
+
+ rv = close(fd);
+ ATF_REQUIRE_EQ(0, rv);
+ rv = unlink(path);
+ ATF_REQUIRE_EQ(0, rv);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, lio_nowait);
+ ATF_TP_ADD_TC(tp, lio_wait_write_then_read);
+
+ return atf_no_error();
+}
diff --git a/lib/libc/sys/t_aio_rw.c b/lib/libc/sys/t_aio_rw.c
new file mode 100644
index 000000000000..e7a5b4fa67d1
--- /dev/null
+++ b/lib/libc/sys/t_aio_rw.c
@@ -0,0 +1,167 @@
+/* $NetBSD: t_aio_rw.c,v 1.1 2025/10/10 15:53:55 christos Exp $ */
+
+/*
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <atf-c.h>
+
+#include <aio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+static int mktemp_file(char *, size_t);
+static void fill_pattern(uint8_t *, size_t, uint8_t);
+static void wait_all(const struct aiocb * const [], size_t);
+
+static int
+mktemp_file(char *path, size_t pathlen)
+{
+ int fd, n;
+
+ n = snprintf(path, pathlen, "t_aio_rw.XXXXXX");
+ ATF_REQUIRE(n > 0 && (size_t)n < pathlen);
+
+ fd = mkstemp(path);
+ ATF_REQUIRE(fd >= 0);
+
+ return fd;
+}
+
+static void
+fill_pattern(uint8_t *buf, size_t len, uint8_t seed)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ buf[i] = (uint8_t)(seed + (i & 0xff));
+ }
+}
+
+static void
+wait_all(const struct aiocb * const list[], size_t nent)
+{
+ size_t i;
+ int pending, rv, error;
+
+ for (;;) {
+ pending = 0;
+
+ for (i = 0; i < nent; i++) {
+ if (list[i] == NULL) {
+ continue;
+ }
+
+ error = aio_error(list[i]);
+ if (error == EINPROGRESS) {
+ pending = 1;
+ }
+ }
+
+ if (!pending) {
+ break;
+ }
+
+ rv = aio_suspend(list, (int)nent, NULL);
+ ATF_REQUIRE_EQ_MSG(0, rv, "aio_suspend failed: %s",
+ strerror(errno));
+ }
+}
+
+/*
+ * write_then_read_back
+ * Write a block then read it back asynchronously and compare.
+ */
+ATF_TC_WITHOUT_HEAD(write_then_read_back);
+ATF_TC_BODY(write_then_read_back, tc)
+{
+ char path[64];
+ int fd, rv;
+ const size_t blksz = 0x2000;
+ uint8_t *wbuf, *rbuf;
+ struct aiocb wcb, rcb;
+ const struct aiocb *wlist[1], *rlist[1];
+
+ fd = mktemp_file(path, sizeof(path));
+
+ wbuf = malloc(blksz);
+ rbuf = calloc(1, blksz);
+ ATF_REQUIRE(wbuf != NULL && rbuf != NULL);
+
+ fill_pattern(wbuf, blksz, 0xA0);
+
+ memset(&wcb, 0, sizeof(wcb));
+ wcb.aio_fildes = fd;
+ wcb.aio_buf = wbuf;
+ wcb.aio_nbytes = blksz;
+ wcb.aio_offset = 0;
+
+ rv = aio_write(&wcb);
+ ATF_REQUIRE_EQ(0, rv);
+ wlist[0] = &wcb;
+ wait_all(wlist, 1);
+
+ ATF_REQUIRE_EQ(0, aio_error(&wcb));
+ ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&wcb));
+
+ memset(&rcb, 0, sizeof(rcb));
+ rcb.aio_fildes = fd;
+ rcb.aio_buf = rbuf;
+ rcb.aio_nbytes = blksz;
+ rcb.aio_offset = 0;
+
+ rv = aio_read(&rcb);
+ ATF_REQUIRE_EQ(0, rv);
+ rlist[0] = &rcb;
+ wait_all(rlist, 1);
+
+ ATF_REQUIRE_EQ(0, aio_error(&rcb));
+ ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&rcb));
+ ATF_REQUIRE_EQ(0, memcmp(wbuf, rbuf, blksz));
+
+ rv = close(fd);
+ ATF_REQUIRE_EQ(0, rv);
+ rv = unlink(path);
+ ATF_REQUIRE_EQ(0, rv);
+
+ free(wbuf);
+ free(rbuf);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, write_then_read_back);
+ return atf_no_error();
+}
diff --git a/lib/libc/sys/t_aio_suspend.c b/lib/libc/sys/t_aio_suspend.c
new file mode 100644
index 000000000000..96b0dc2cf6d0
--- /dev/null
+++ b/lib/libc/sys/t_aio_suspend.c
@@ -0,0 +1,170 @@
+/* $NetBSD: t_aio_suspend.c,v 1.1 2025/10/10 15:53:55 christos Exp $ */
+
+/*
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <atf-c.h>
+
+#include <aio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+static int mktemp_file(char *, size_t);
+static void fill_pattern(uint8_t *, size_t, uint8_t);
+static void wait_cb(struct aiocb *);
+
+static int
+mktemp_file(char *path, size_t pathlen)
+{
+ int fd, n;
+
+ n = snprintf(path, pathlen, "t_aio_suspend.XXXXXX");
+ ATF_REQUIRE(n > 0 && (size_t)n < pathlen);
+
+ fd = mkstemp(path);
+ ATF_REQUIRE(fd >= 0);
+
+ return fd;
+}
+
+static void
+fill_pattern(uint8_t *buf, size_t len, uint8_t seed)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ buf[i] = (uint8_t)(seed + (i & 0xff));
+ }
+}
+
+static void
+wait_cb(struct aiocb *cb)
+{
+ const struct aiocb *one[1];
+ int rv;
+
+ one[0] = cb;
+ while (aio_error(cb) == EINPROGRESS) {
+ rv = aio_suspend(one, 1, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+ }
+ if (aio_error(cb) == 0) {
+ aio_return(cb);
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(suspend_any);
+ATF_TC_BODY(suspend_any, tc)
+{
+ char path[64];
+ int fd, rv;
+ const size_t blksz = 4096;
+ uint8_t *buf0, *buf1;
+ struct aiocb cb0, cb1;
+ const struct aiocb *list[2];
+ int done;
+
+ fd = mktemp_file(path, sizeof(path));
+
+ buf0 = malloc(blksz);
+ buf1 = malloc(blksz);
+ ATF_REQUIRE(buf0 != NULL && buf1 != NULL);
+ fill_pattern(buf0, blksz, 0x20);
+ fill_pattern(buf1, blksz, 0x40);
+
+ memset(&cb0, 0, sizeof(cb0));
+ cb0.aio_fildes = fd;
+ cb0.aio_buf = buf0;
+ cb0.aio_nbytes = blksz;
+ cb0.aio_offset = 0;
+
+ memset(&cb1, 0, sizeof(cb1));
+ cb1.aio_fildes = fd;
+ cb1.aio_buf = buf1;
+ cb1.aio_nbytes = blksz;
+ cb1.aio_offset = blksz;
+
+ ATF_REQUIRE_EQ(0, aio_write(&cb0));
+ ATF_REQUIRE_EQ(0, aio_write(&cb1));
+
+ list[0] = &cb0;
+ list[1] = &cb1;
+
+ rv = aio_suspend(list, 2, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+
+ done = 0;
+ if (aio_error(&cb0) != EINPROGRESS) {
+ done++;
+ if (aio_error(&cb0) == 0) {
+ ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&cb0));
+ } else {
+ ATF_REQUIRE_EQ(ECANCELED, aio_error(&cb0));
+ ATF_REQUIRE_EQ(-1, aio_return(&cb0));
+ }
+ }
+ if (aio_error(&cb1) != EINPROGRESS) {
+ done++;
+ if (aio_error(&cb1) == 0) {
+ ATF_REQUIRE_EQ((ssize_t)blksz, aio_return(&cb1));
+ } else {
+ ATF_REQUIRE_EQ(ECANCELED, aio_error(&cb1));
+ ATF_REQUIRE_EQ(-1, aio_return(&cb1));
+ }
+ }
+ ATF_REQUIRE(done >= 1);
+
+ if (aio_error(&cb0) == EINPROGRESS) {
+ wait_cb(&cb0);
+ }
+ if (aio_error(&cb1) == EINPROGRESS) {
+ wait_cb(&cb1);
+ }
+
+ rv = close(fd);
+ ATF_REQUIRE_EQ(0, rv);
+ rv = unlink(path);
+ ATF_REQUIRE_EQ(0, rv);
+
+ free(buf0);
+ free(buf1);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, suspend_any);
+ return atf_no_error();
+}
diff --git a/lib/libc/sys/t_ptrace_kill.c b/lib/libc/sys/t_ptrace_kill.c
new file mode 100644
index 000000000000..fdd6298c2a8a
--- /dev/null
+++ b/lib/libc/sys/t_ptrace_kill.c
@@ -0,0 +1,131 @@
+/* $NetBSD: t_ptrace_kill.c,v 1.2 2025/05/02 02:24:32 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2024 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: t_ptrace_kill.c,v 1.2 2025/05/02 02:24:32 riastradh Exp $");
+
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+#include <err.h>
+#include <atf-c.h>
+
+#define SYSCALL(a, b) ATF_REQUIRE_EQ_MSG(a, b, "%s got %s", #a, strerror(errno))
+
+ATF_TC(pt_kill);
+ATF_TC_HEAD(pt_kill, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test PT_KILL of a PT_STOP'ed process");
+}
+
+static void
+child(int *fdto, int *fdfrom)
+{
+ char p = '2', q;
+ printf("%d: born\n", getpid());
+ write(fdfrom[1], &p, 1);
+ read(fdto[0], &q, 1);
+ printf("%d: seppuku %c\n", getpid(), q);
+ write(fdfrom[1], &p, 1);
+ read(fdto[0], &q, 1);
+// *(int *)1 = 0;
+// kill(getpid(), SIGSEGV);
+// kill(getpid(), SIGSTOP);
+ for (;;)
+ sleep(1);
+
+}
+
+static void *
+waitthread(void *pidp)
+{
+ int status = 0;
+ pid_t rpid, pid;
+
+ pid = *(pid_t *)pidp;
+ printf("waiting for %d\n", pid);
+ while ((rpid = waitpid(pid, &status, 0)) != pid) {
+ printf("waitpid %d = %d status = %#x", pid, rpid, status);
+ }
+ printf("done waitpid %d = %d status = %#x", pid, rpid, status);
+ return NULL;
+}
+
+ATF_TC_BODY(pt_kill, tc)
+{
+ pid_t pid;
+ int fdto[2], fdfrom[2];
+ char p = '1', q;
+ int status;
+ pthread_t thread;
+
+ SYSCALL(pipe(fdto), 0);
+ SYSCALL(pipe(fdfrom), 0);
+ switch (pid = fork()) {
+ case 0:
+ child(fdto, fdfrom);
+ break;
+ case -1:
+ err(EXIT_FAILURE, "fork failed");
+ default:
+ SYSCALL(pthread_create(&thread, NULL, waitthread, &pid), 0);
+ sleep(1); // XXX: too lazy to sync properly
+ SYSCALL(read(fdfrom[0], &q, 1), 1);
+ printf("%d: read %c\n", pid, q);
+ SYSCALL(ptrace(PT_ATTACH, pid, NULL, 0), 0);
+ printf("%d: attached\n", pid);
+ SYSCALL(write(fdto[1], &p, 1), 1);
+ waitpid(pid, NULL, WNOHANG);
+ printf("%d: sent\n", pid);
+ SYSCALL(ptrace(PT_CONTINUE, pid, (void *)1, 0), 0);
+ SYSCALL(read(fdfrom[0], &p, 1), 1);
+ printf("%d: received\n", pid);
+ SYSCALL(ptrace(PT_STOP, pid, NULL, 0), 0);
+ SYSCALL(ptrace(PT_KILL, pid, NULL, 0), 0);
+ SYSCALL(waitpid(pid, &status, 0), pid);
+ ATF_REQUIRE(status == 9);
+ break;
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, pt_kill);
+
+ return atf_no_error();
+}
diff --git a/lib/libexecinfo/t_backtrace_sandbox.c b/lib/libexecinfo/t_backtrace_sandbox.c
new file mode 100644
index 000000000000..51d650c3424a
--- /dev/null
+++ b/lib/libexecinfo/t_backtrace_sandbox.c
@@ -0,0 +1,88 @@
+/* $NetBSD: t_backtrace_sandbox.c,v 1.3 2025/01/30 16:13:51 christos Exp $ */
+
+/*-
+ * Copyright (c) 2025 Kyle Evans <kevans@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_backtrace_sandbox.c,v 1.3 2025/01/30 16:13:51 christos Exp $");
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#ifdef __FreeBSD__
+#include <sys/capsicum.h>
+#define __arraycount(a) nitems(a)
+#endif
+
+#include <execinfo.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#define BT_FUNCTIONS 10
+
+ATF_TC(backtrace_sandbox);
+ATF_TC_HEAD(backtrace_sandbox, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test backtrace_sandbox_init(3) in sandbox");
+#ifndef __FreeBSD__
+ atf_tc_set_md_var(tc, "require.user", "root");
+#endif
+}
+
+ATF_TC_BODY(backtrace_sandbox, tc)
+{
+ void *addr[BT_FUNCTIONS];
+ char **syms;
+ size_t frames;
+ pid_t pid;
+ int status;
+
+ frames = backtrace(addr, __arraycount(addr));
+ ATF_REQUIRE(frames > 0);
+
+ syms = backtrace_symbols_fmt(addr, frames, "%n");
+ ATF_REQUIRE(strcmp(syms[0], "atfu_backtrace_sandbox_body") == 0);
+
+ pid = fork();
+ ATF_REQUIRE(pid >= 0);
+
+ if (pid == 0) {
+
+ backtrace_sandbox_init();
+#ifdef __FreeBSD__
+ cap_enter();
+#else
+ if (chroot("/tmp") != 0)
+ _exit(EXIT_FAILURE);
+#endif
+
+ syms = backtrace_symbols_fmt(addr, frames, "%n");
+ if (strcmp(syms[0], "atfu_backtrace_sandbox_body") != 0)
+ _exit(EXIT_FAILURE);
+
+ backtrace_sandbox_fini();
+
+ _exit(EXIT_SUCCESS);
+ }
+
+ (void)wait(&status);
+
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != EXIT_SUCCESS)
+ atf_tc_fail("resolving symbols in chroot failed");
+
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, backtrace_sandbox);
+
+ return atf_no_error();
+}
diff --git a/lib/libm/t_errhandling.c b/lib/libm/t_errhandling.c
new file mode 100644
index 000000000000..7c95afe22121
--- /dev/null
+++ b/lib/libm/t_errhandling.c
@@ -0,0 +1,97 @@
+/* $NetBSD: t_errhandling.c,v 1.3 2024/09/10 17:36:12 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define __TEST_FENV
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_errhandling.c,v 1.3 2024/09/10 17:36:12 riastradh Exp $");
+
+#include <atf-c.h>
+#include <errno.h>
+#include <fenv.h>
+#include <math.h>
+
+ATF_TC(log);
+ATF_TC_HEAD(log, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "log of invalid");
+}
+ATF_TC_BODY(log, tc)
+{
+ static const struct {
+#ifdef __HAVE_FENV
+ double x;
+ int e;
+#define C(x, e) { x, e }
+#else
+ double x;
+#define C(x, e) { x }
+#endif
+ } cases[] = {
+ C(0, FE_DIVBYZERO),
+ C(-0., FE_DIVBYZERO),
+ C(-1, FE_INVALID),
+ C(-HUGE_VAL, FE_INVALID),
+ };
+ volatile double y;
+#ifdef __HAVE_FENV
+ int except;
+#endif
+ unsigned i;
+
+ for (i = 0; i < __arraycount(cases); i++) {
+ const volatile double x = cases[i].x;
+
+#ifdef __HAVE_FENV
+ feclearexcept(FE_ALL_EXCEPT);
+#endif
+ errno = 0;
+ y = log(x);
+ if (math_errhandling & MATH_ERREXCEPT) {
+#ifdef __HAVE_FENV
+ ATF_CHECK_MSG(((except = fetestexcept(FE_ALL_EXCEPT)) &
+ cases[i].e) != 0,
+ "expected=0x%x actual=0x%x", cases[i].e, except);
+#else
+ atf_tc_fail_nonfatal("MATH_ERREXCEPT but no fenv.h");
+#endif
+ }
+ if (math_errhandling & MATH_ERRNO)
+ ATF_CHECK_EQ_MSG(errno, EDOM, "errno=%d", errno);
+ }
+
+ __USE(y);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, log);
+
+ return atf_no_error();
+}
diff --git a/lib/libm/t_next.c b/lib/libm/t_next.c
new file mode 100644
index 000000000000..22363f92daf2
--- /dev/null
+++ b/lib/libm/t_next.c
@@ -0,0 +1,887 @@
+/* $NetBSD: t_next.c,v 1.8 2025/04/07 01:31:18 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_next.c,v 1.8 2025/04/07 01:31:18 riastradh Exp $");
+
+#include <atf-c.h>
+#include <float.h>
+#include <math.h>
+
+#ifdef __vax__ /* XXX PR 57881: vax libm is missing various symbols */
+
+ATF_TC(vaxafter);
+ATF_TC_HEAD(vaxafter, tc)
+{
+
+ atf_tc_set_md_var(tc, "descr", "vax nextafter/nexttoward reminder");
+}
+ATF_TC_BODY(vaxafter, tc)
+{
+
+ atf_tc_expect_fail("PR 57881: vax libm is missing various symbols");
+ atf_tc_fail("missing nextafter{,f,l} and nexttoward{,f,l} on vax");
+}
+
+#else /* !__vax__ */
+
+#define CHECK(i, next, x, d, y) do \
+{ \
+ volatile __typeof__(x) check_x = (x); \
+ volatile __typeof__(d) check_d = (d); \
+ volatile __typeof__(y) check_y = (y); \
+ const volatile __typeof__(y) check_tmp = (next)(check_x, check_d); \
+ ATF_CHECK_MSG(check_tmp == check_y, \
+ "[%u] %s(%s=%La=%Lg, %s=%La=%Lg)=%La=%Lg != %s=%La=%Lg", \
+ (i), #next, \
+ #x, (long double)check_x, (long double)check_x, \
+ #d, (long double)check_d, (long double)check_d, \
+ (long double)check_tmp, (long double)check_tmp, \
+ #y, (long double)check_y, (long double)check_y); \
+} while (0)
+
+/*
+ * check(x, n)
+ *
+ * x[0], x[1], ..., x[n - 1] are consecutive double floating-point
+ * numbers. Verify nextafter and nexttoward follow exactly this
+ * sequence, forward and back, and in negative.
+ */
+static void
+check(const double *x, unsigned n)
+{
+ unsigned i;
+
+ for (i = 0; i < n; i++) {
+ CHECK(i, nextafter, x[i], x[i], x[i]);
+ CHECK(i, nexttoward, x[i], x[i], x[i]);
+ CHECK(i, nextafter, -x[i], -x[i], -x[i]);
+ CHECK(i, nexttoward, -x[i], -x[i], -x[i]);
+ }
+
+ for (i = 0; i < n - 1; i++) {
+ ATF_REQUIRE_MSG(x[i] < x[i + 1], "i=%u", i);
+
+ if (isnormal(x[i])) {
+ CHECK(i, nexttoward, x[i], x[i]*(1 + LDBL_EPSILON),
+ x[i + 1]);
+ }
+
+ CHECK(i, nextafter, x[i], x[i + 1], x[i + 1]);
+ CHECK(i, nexttoward, x[i], x[i + 1], x[i + 1]);
+ CHECK(i, nextafter, x[i], x[n - 1], x[i + 1]);
+ CHECK(i, nexttoward, x[i], x[n - 1], x[i + 1]);
+ CHECK(i, nextafter, x[i], INFINITY, x[i + 1]);
+ CHECK(i, nexttoward, x[i], INFINITY, x[i + 1]);
+
+ CHECK(i, nextafter, -x[i], -x[i + 1], -x[i + 1]);
+ CHECK(i, nexttoward, -x[i], -x[i + 1], -x[i + 1]);
+ CHECK(i, nextafter, -x[i], -x[n - 1], -x[i + 1]);
+ CHECK(i, nexttoward, -x[i], -x[n - 1], -x[i + 1]);
+ CHECK(i, nextafter, -x[i], -INFINITY, -x[i + 1]);
+ CHECK(i, nexttoward, -x[i], -INFINITY, -x[i + 1]);
+ }
+
+ for (i = n; i --> 1;) {
+ ATF_REQUIRE_MSG(x[i - 1] < x[i], "i=%u", i);
+
+#ifdef __HAVE_LONG_DOUBLE
+ if (isnormal(x[i])) {
+ CHECK(i, nexttoward, x[i], x[i]*(1 - LDBL_EPSILON/2),
+ x[i - 1]);
+ }
+#endif
+
+ CHECK(i, nextafter, x[i], x[i - 1], x[i - 1]);
+ CHECK(i, nexttoward, x[i], x[i - 1], x[i - 1]);
+ CHECK(i, nextafter, x[i], x[0], x[i - 1]);
+ CHECK(i, nexttoward, x[i], x[0], x[i - 1]);
+ CHECK(i, nextafter, x[i], +0., x[i - 1]);
+ CHECK(i, nexttoward, x[i], +0., x[i - 1]);
+ CHECK(i, nextafter, x[i], -0., x[i - 1]);
+ CHECK(i, nexttoward, x[i], -0., x[i - 1]);
+ CHECK(i, nextafter, x[i], -x[0], x[i - 1]);
+ CHECK(i, nexttoward, x[i], -x[0], x[i - 1]);
+ CHECK(i, nextafter, x[i], -x[i], x[i - 1]);
+ CHECK(i, nexttoward, x[i], -x[i], x[i - 1]);
+ CHECK(i, nextafter, x[i], -INFINITY, x[i - 1]);
+ CHECK(i, nexttoward, x[i], -INFINITY, x[i - 1]);
+
+ CHECK(i, nextafter, -x[i], -x[i - 1], -x[i - 1]);
+ CHECK(i, nexttoward, -x[i], -x[i - 1], -x[i - 1]);
+ CHECK(i, nextafter, -x[i], -x[0], -x[i - 1]);
+ CHECK(i, nexttoward, -x[i], -x[0], -x[i - 1]);
+ CHECK(i, nextafter, -x[i], -0., -x[i - 1]);
+ CHECK(i, nexttoward, -x[i], -0., -x[i - 1]);
+ CHECK(i, nextafter, -x[i], +0., -x[i - 1]);
+ CHECK(i, nexttoward, -x[i], +0., -x[i - 1]);
+ CHECK(i, nextafter, -x[i], x[0], -x[i - 1]);
+ CHECK(i, nexttoward, -x[i], x[0], -x[i - 1]);
+ CHECK(i, nextafter, -x[i], INFINITY, -x[i - 1]);
+ CHECK(i, nexttoward, -x[i], INFINITY, -x[i - 1]);
+ }
+}
+
+/*
+ * checkf(x, n)
+ *
+ * x[0], x[1], ..., x[n - 1] are consecutive single floating-point
+ * numbers. Verify nextafterf and nexttowardf follow exactly this
+ * sequence, forward and back, and in negative.
+ */
+static void
+checkf(const float *x, unsigned n)
+{
+ unsigned i;
+
+ for (i = 0; i < n; i++) {
+ CHECK(i, nextafterf, x[i], x[i], x[i]);
+ CHECK(i, nexttowardf, x[i], x[i], x[i]);
+ CHECK(i, nextafterf, -x[i], -x[i], -x[i]);
+ CHECK(i, nexttowardf, -x[i], -x[i], -x[i]);
+ }
+
+ for (i = 0; i < n - 1; i++) {
+ ATF_REQUIRE_MSG(x[i] < x[i + 1], "i=%u", i);
+
+ if (isnormal(x[i])) {
+ CHECK(i, nexttowardf, x[i], x[i]*(1 + LDBL_EPSILON),
+ x[i + 1]);
+ }
+
+ CHECK(i, nextafterf, x[i], x[i + 1], x[i + 1]);
+ CHECK(i, nexttowardf, x[i], x[i + 1], x[i + 1]);
+ CHECK(i, nextafterf, x[i], x[n - 1], x[i + 1]);
+ CHECK(i, nexttowardf, x[i], x[n - 1], x[i + 1]);
+ CHECK(i, nextafterf, x[i], INFINITY, x[i + 1]);
+ CHECK(i, nexttowardf, x[i], INFINITY, x[i + 1]);
+
+ CHECK(i, nextafterf, -x[i], -x[i + 1], -x[i + 1]);
+ CHECK(i, nexttowardf, -x[i], -x[i + 1], -x[i + 1]);
+ CHECK(i, nextafterf, -x[i], -x[n - 1], -x[i + 1]);
+ CHECK(i, nexttowardf, -x[i], -x[n - 1], -x[i + 1]);
+ CHECK(i, nextafterf, -x[i], -INFINITY, -x[i + 1]);
+ CHECK(i, nexttowardf, -x[i], -INFINITY, -x[i + 1]);
+ }
+
+ for (i = n; i --> 1;) {
+ ATF_REQUIRE_MSG(x[i - 1] < x[i], "i=%u", i);
+
+ if (isnormal(x[i])) {
+ CHECK(i, nexttowardf, x[i], x[i]*(1 - LDBL_EPSILON/2),
+ x[i - 1]);
+ }
+
+ CHECK(i, nextafterf, x[i], x[i - 1], x[i - 1]);
+ CHECK(i, nexttowardf, x[i], x[i - 1], x[i - 1]);
+ CHECK(i, nextafterf, x[i], x[0], x[i - 1]);
+ CHECK(i, nexttowardf, x[i], x[0], x[i - 1]);
+ CHECK(i, nextafterf, x[i], +0., x[i - 1]);
+ CHECK(i, nexttowardf, x[i], +0., x[i - 1]);
+ CHECK(i, nextafterf, x[i], -0., x[i - 1]);
+ CHECK(i, nexttowardf, x[i], -0., x[i - 1]);
+ CHECK(i, nextafterf, x[i], -x[0], x[i - 1]);
+ CHECK(i, nexttowardf, x[i], -x[0], x[i - 1]);
+ CHECK(i, nextafterf, x[i], -x[i], x[i - 1]);
+ CHECK(i, nexttowardf, x[i], -x[i], x[i - 1]);
+ CHECK(i, nextafterf, x[i], -INFINITY, x[i - 1]);
+ CHECK(i, nexttowardf, x[i], -INFINITY, x[i - 1]);
+
+ CHECK(i, nextafterf, -x[i], -x[i - 1], -x[i - 1]);
+ CHECK(i, nexttowardf, -x[i], -x[i - 1], -x[i - 1]);
+ CHECK(i, nextafterf, -x[i], -x[0], -x[i - 1]);
+ CHECK(i, nexttowardf, -x[i], -x[0], -x[i - 1]);
+ CHECK(i, nextafterf, -x[i], -0., -x[i - 1]);
+ CHECK(i, nexttowardf, -x[i], -0., -x[i - 1]);
+ CHECK(i, nextafterf, -x[i], +0., -x[i - 1]);
+ CHECK(i, nexttowardf, -x[i], +0., -x[i - 1]);
+ CHECK(i, nextafterf, -x[i], x[0], -x[i - 1]);
+ CHECK(i, nexttowardf, -x[i], x[0], -x[i - 1]);
+ CHECK(i, nextafterf, -x[i], INFINITY, -x[i - 1]);
+ CHECK(i, nexttowardf, -x[i], INFINITY, -x[i - 1]);
+ }
+}
+
+/*
+ * checkl(x, n)
+ *
+ * x[0], x[1], ..., x[n - 1] are consecutive long double
+ * floating-point numbers. Verify nextafterl and nexttowardl
+ * follow exactly this sequence, forward and back, and in
+ * negative.
+ */
+static void
+checkl(const long double *x, unsigned n)
+{
+ unsigned i;
+
+ for (i = 0; i < n; i++) {
+ CHECK(i, nextafterl, x[i], x[i], x[i]);
+ CHECK(i, nexttowardl, x[i], x[i], x[i]);
+ CHECK(i, nextafterl, -x[i], -x[i], -x[i]);
+ CHECK(i, nexttowardl, -x[i], -x[i], -x[i]);
+ }
+
+ for (i = 0; i < n - 1; i++) {
+ ATF_REQUIRE_MSG(x[i] < x[i + 1], "i=%u", i);
+
+ CHECK(i, nextafterl, x[i], x[i + 1], x[i + 1]);
+ CHECK(i, nexttowardl, x[i], x[i + 1], x[i + 1]);
+ CHECK(i, nextafterl, x[i], x[n - 1], x[i + 1]);
+ CHECK(i, nexttowardl, x[i], x[n - 1], x[i + 1]);
+ CHECK(i, nextafterl, x[i], INFINITY, x[i + 1]);
+ CHECK(i, nexttowardl, x[i], INFINITY, x[i + 1]);
+
+ CHECK(i, nextafterl, -x[i], -x[i + 1], -x[i + 1]);
+ CHECK(i, nexttowardl, -x[i], -x[i + 1], -x[i + 1]);
+ CHECK(i, nextafterl, -x[i], -x[n - 1], -x[i + 1]);
+ CHECK(i, nexttowardl, -x[i], -x[n - 1], -x[i + 1]);
+ CHECK(i, nextafterl, -x[i], -INFINITY, -x[i + 1]);
+ CHECK(i, nexttowardl, -x[i], -INFINITY, -x[i + 1]);
+ }
+
+ for (i = n; i --> 1;) {
+ ATF_REQUIRE_MSG(x[i - 1] < x[i], "i=%u", i);
+
+ CHECK(i, nextafterl, x[i], x[i - 1], x[i - 1]);
+ CHECK(i, nexttowardl, x[i], x[i - 1], x[i - 1]);
+ CHECK(i, nextafterl, x[i], x[0], x[i - 1]);
+ CHECK(i, nexttowardl, x[i], x[0], x[i - 1]);
+ CHECK(i, nextafterl, x[i], +0., x[i - 1]);
+ CHECK(i, nexttowardl, x[i], +0., x[i - 1]);
+ CHECK(i, nextafterl, x[i], -0., x[i - 1]);
+ CHECK(i, nexttowardl, x[i], -0., x[i - 1]);
+ CHECK(i, nextafterl, x[i], -x[0], x[i - 1]);
+ CHECK(i, nexttowardl, x[i], -x[0], x[i - 1]);
+ CHECK(i, nextafterl, x[i], -x[i], x[i - 1]);
+ CHECK(i, nexttowardl, x[i], -x[i], x[i - 1]);
+ CHECK(i, nextafterl, x[i], -INFINITY, x[i - 1]);
+ CHECK(i, nexttowardl, x[i], -INFINITY, x[i - 1]);
+
+ CHECK(i, nextafterl, -x[i], -x[i - 1], -x[i - 1]);
+ CHECK(i, nexttowardl, -x[i], -x[i - 1], -x[i - 1]);
+ CHECK(i, nextafterl, -x[i], -x[0], -x[i - 1]);
+ CHECK(i, nexttowardl, -x[i], -x[0], -x[i - 1]);
+ CHECK(i, nextafterl, -x[i], -0., -x[i - 1]);
+ CHECK(i, nexttowardl, -x[i], -0., -x[i - 1]);
+ CHECK(i, nextafterl, -x[i], +0., -x[i - 1]);
+ CHECK(i, nexttowardl, -x[i], +0., -x[i - 1]);
+ CHECK(i, nextafterl, -x[i], x[0], -x[i - 1]);
+ CHECK(i, nexttowardl, -x[i], x[0], -x[i - 1]);
+ CHECK(i, nextafterl, -x[i], INFINITY, -x[i - 1]);
+ CHECK(i, nexttowardl, -x[i], INFINITY, -x[i - 1]);
+ }
+}
+
+ATF_TC(next_nan);
+ATF_TC_HEAD(next_nan, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "nextafter/nexttoward on NaN");
+}
+ATF_TC_BODY(next_nan, tc)
+{
+#ifdef NAN
+ /* XXX verify the NaN is quiet */
+ ATF_CHECK(isnan(nextafter(NAN, 0)));
+ ATF_CHECK(isnan(nexttoward(NAN, 0)));
+ ATF_CHECK(isnan(nextafter(0, NAN)));
+ ATF_CHECK(isnan(nexttoward(0, NAN)));
+#else
+ atf_tc_skip("no NaNs on this architecture");
+#endif
+}
+
+ATF_TC(next_signed_0);
+ATF_TC_HEAD(next_signed_0, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "nextafter/nexttoward on signed 0");
+}
+ATF_TC_BODY(next_signed_0, tc)
+{
+ volatile double z_pos = +0.;
+ volatile double z_neg = -0.;
+#if __DBL_HAS_DENORM__
+ volatile double m = __DBL_DENORM_MIN__;
+#else
+ volatile double m = DBL_MIN;
+#endif
+
+ if (signbit(z_pos) == signbit(z_neg))
+ atf_tc_skip("no signed zeroes on this architecture");
+
+ /*
+ * `nextUp(x) is the least floating-point number in the format
+ * of x that compares greater than x. [...] nextDown(x) is
+ * -nextUp(-x).'
+ * --IEEE 754-2019, 5.3.1 General operations, p. 19
+ *
+ * Verify that nextafter and nexttoward, which implement the
+ * nextUp and nextDown operations, obey this rule and don't
+ * send -0 to +0 or +0 to -0, respectively.
+ */
+
+ CHECK(0, nextafter, z_neg, +INFINITY, m);
+ CHECK(1, nexttoward, z_neg, +INFINITY, m);
+ CHECK(2, nextafter, z_pos, +INFINITY, m);
+ CHECK(3, nexttoward, z_pos, +INFINITY, m);
+
+ CHECK(4, nextafter, z_pos, -INFINITY, -m);
+ CHECK(5, nexttoward, z_pos, -INFINITY, -m);
+ CHECK(6, nextafter, z_neg, -INFINITY, -m);
+ CHECK(7, nexttoward, z_neg, -INFINITY, -m);
+
+ /*
+ * `If x is the negative number of least magnitude in x's
+ * format, nextUp(x) is -0.'
+ * --IEEE 754-2019, 5.3.1 General operations, p. 19
+ *
+ * Verify that nextafter and nexttoward return the correctly
+ * signed zero.
+ */
+ CHECK(8, nextafter, -m, +INFINITY, 0);
+ CHECK(9, nexttoward, -m, +INFINITY, 0);
+ ATF_CHECK(signbit(nextafter(-m, +INFINITY)) != 0);
+ CHECK(10, nextafter, m, -INFINITY, 0);
+ CHECK(11, nexttoward, m, -INFINITY, 0);
+ ATF_CHECK(signbit(nextafter(m, -INFINITY)) == 0);
+}
+
+ATF_TC(next_near_0);
+ATF_TC_HEAD(next_near_0, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "nextafter/nexttoward near 0");
+}
+ATF_TC_BODY(next_near_0, tc)
+{
+ static const double x[] = {
+ [0] = 0,
+#if __DBL_HAS_DENORM__
+ [1] = __DBL_DENORM_MIN__,
+ [2] = 2*__DBL_DENORM_MIN__,
+ [3] = 3*__DBL_DENORM_MIN__,
+ [4] = 4*__DBL_DENORM_MIN__,
+#else
+ [1] = DBL_MIN,
+ [2] = DBL_MIN*(1 + DBL_EPSILON),
+ [3] = DBL_MIN*(1 + 2*DBL_EPSILON),
+ [4] = DBL_MIN*(1 + 3*DBL_EPSILON),
+#endif
+ };
+
+ check(x, __arraycount(x));
+}
+
+ATF_TC(next_near_sub_normal);
+ATF_TC_HEAD(next_near_sub_normal, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "nextafter/nexttoward near the subnormal/normal boundary");
+}
+ATF_TC_BODY(next_near_sub_normal, tc)
+{
+#if __DBL_HAS_DENORM__
+ static const double x[] = {
+ [0] = DBL_MIN - 3*__DBL_DENORM_MIN__,
+ [1] = DBL_MIN - 2*__DBL_DENORM_MIN__,
+ [2] = DBL_MIN - __DBL_DENORM_MIN__,
+ [3] = DBL_MIN,
+ [4] = DBL_MIN + __DBL_DENORM_MIN__,
+ [5] = DBL_MIN + 2*__DBL_DENORM_MIN__,
+ [6] = DBL_MIN + 3*__DBL_DENORM_MIN__,
+ };
+
+ check(x, __arraycount(x));
+#else /* !__DBL_HAS_DENORM__ */
+ atf_tc_skip("no subnormals on this architecture");
+#endif /* !__DBL_HAS_DENORM__ */
+}
+
+ATF_TC(next_near_1);
+ATF_TC_HEAD(next_near_1, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "nextafter/nexttoward near 1");
+}
+ATF_TC_BODY(next_near_1, tc)
+{
+ static const double x[] = {
+ [0] = 1 - 3*DBL_EPSILON/2,
+ [1] = 1 - 2*DBL_EPSILON/2,
+ [2] = 1 - DBL_EPSILON/2,
+ [3] = 1,
+ [4] = 1 + DBL_EPSILON,
+ [5] = 1 + 2*DBL_EPSILON,
+ [6] = 1 + 3*DBL_EPSILON,
+ };
+
+ check(x, __arraycount(x));
+}
+
+ATF_TC(next_near_1_5);
+ATF_TC_HEAD(next_near_1_5, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "nextafter/nexttoward near 1.5");
+}
+ATF_TC_BODY(next_near_1_5, tc)
+{
+ static const double x[] = {
+ [0] = 1.5 - 3*DBL_EPSILON,
+ [1] = 1.5 - 2*DBL_EPSILON,
+ [2] = 1.5 - DBL_EPSILON,
+ [3] = 1.5,
+ [4] = 1.5 + DBL_EPSILON,
+ [5] = 1.5 + 2*DBL_EPSILON,
+ [6] = 1.5 + 3*DBL_EPSILON,
+ };
+
+ check(x, __arraycount(x));
+}
+
+ATF_TC(next_near_infinity);
+ATF_TC_HEAD(next_near_infinity, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "nextafter/nexttoward near infinity");
+}
+ATF_TC_BODY(next_near_infinity, tc)
+{
+ static const double x[] = {
+ [0] = DBL_MAX,
+ [1] = INFINITY,
+ };
+ volatile double t;
+
+ if (!isinf(INFINITY))
+ atf_tc_skip("no infinities on this architecture");
+
+ check(x, __arraycount(x));
+
+ ATF_CHECK_EQ_MSG((t = nextafter(INFINITY, INFINITY)), INFINITY,
+ "t=%a=%g", t, t);
+ ATF_CHECK_EQ_MSG((t = nextafter(-INFINITY, -INFINITY)), -INFINITY,
+ "t=%a=%g", t, t);
+}
+
+ATF_TC(nextf_nan);
+ATF_TC_HEAD(nextf_nan, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "nextafterf/nexttowardf on NaN");
+}
+ATF_TC_BODY(nextf_nan, tc)
+{
+#ifdef NAN
+ /* XXX verify the NaN is quiet */
+ ATF_CHECK(isnan(nextafterf(NAN, 0)));
+ ATF_CHECK(isnan(nexttowardf(NAN, 0)));
+ ATF_CHECK(isnan(nextafterf(0, NAN)));
+ ATF_CHECK(isnan(nexttowardf(0, NAN)));
+#else
+ atf_tc_skip("no NaNs on this architecture");
+#endif
+}
+
+ATF_TC(nextf_signed_0);
+ATF_TC_HEAD(nextf_signed_0, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "nextafterf/nexttowardf on signed 0");
+}
+ATF_TC_BODY(nextf_signed_0, tc)
+{
+ volatile float z_pos = +0.;
+ volatile float z_neg = -0.;
+#if __FLT_HAS_DENORM__
+ volatile float m = __FLT_DENORM_MIN__;
+#else
+ volatile float m = FLT_MIN;
+#endif
+
+ if (signbit(z_pos) == signbit(z_neg))
+ atf_tc_skip("no signed zeroes on this architecture");
+
+ /*
+ * `nextUp(x) is the least floating-point number in the format
+ * of x that compares greater than x. [...] nextDown(x) is
+ * -nextUp(-x).'
+ * --IEEE 754-2019, 5.3.1 General operations, p. 19
+ *
+ * Verify that nextafterf and nexttowardf, which implement the
+ * nextUp and nextDown operations, obey this rule and don't
+ * send -0 to +0 or +0 to -0, respectively.
+ */
+
+ CHECK(0, nextafterf, z_neg, +INFINITY, m);
+ CHECK(1, nexttowardf, z_neg, +INFINITY, m);
+ CHECK(2, nextafterf, z_pos, +INFINITY, m);
+ CHECK(3, nexttowardf, z_pos, +INFINITY, m);
+
+ CHECK(4, nextafterf, z_pos, -INFINITY, -m);
+ CHECK(5, nexttowardf, z_pos, -INFINITY, -m);
+ CHECK(6, nextafterf, z_neg, -INFINITY, -m);
+ CHECK(7, nexttowardf, z_neg, -INFINITY, -m);
+
+ /*
+ * `If x is the negative number of least magnitude in x's
+ * format, nextUp(x) is -0.'
+ * --IEEE 754-2019, 5.3.1 General operations, p. 19
+ */
+ CHECK(8, nextafterf, -m, +INFINITY, 0);
+ CHECK(9, nexttowardf, -m, +INFINITY, 0);
+ ATF_CHECK(signbit(nextafterf(-m, +INFINITY)) != 0);
+ CHECK(10, nextafterf, m, -INFINITY, 0);
+ CHECK(11, nexttowardf, m, -INFINITY, 0);
+ ATF_CHECK(signbit(nextafterf(m, -INFINITY)) == 0);
+}
+
+ATF_TC(nextf_near_0);
+ATF_TC_HEAD(nextf_near_0, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "nextafterf/nexttowardf near 0");
+}
+ATF_TC_BODY(nextf_near_0, tc)
+{
+ static const float x[] = {
+ [0] = 0,
+#if __FLT_HAS_DENORM__
+ [1] = __FLT_DENORM_MIN__,
+ [2] = 2*__FLT_DENORM_MIN__,
+ [3] = 3*__FLT_DENORM_MIN__,
+ [4] = 4*__FLT_DENORM_MIN__,
+#else
+ [1] = FLT_MIN,
+ [2] = FLT_MIN*(1 + FLT_EPSILON),
+ [3] = FLT_MIN*(1 + 2*FLT_EPSILON),
+ [4] = FLT_MIN*(1 + 3*FLT_EPSILON),
+#endif
+ };
+
+ checkf(x, __arraycount(x));
+}
+
+ATF_TC(nextf_near_sub_normal);
+ATF_TC_HEAD(nextf_near_sub_normal, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "nextafterf/nexttowardf near the subnormal/normal boundary");
+}
+ATF_TC_BODY(nextf_near_sub_normal, tc)
+{
+#if __FLT_HAS_DENORM__
+ static const float x[] = {
+ [0] = FLT_MIN - 3*__FLT_DENORM_MIN__,
+ [1] = FLT_MIN - 2*__FLT_DENORM_MIN__,
+ [2] = FLT_MIN - __FLT_DENORM_MIN__,
+ [3] = FLT_MIN,
+ [4] = FLT_MIN + __FLT_DENORM_MIN__,
+ [5] = FLT_MIN + 2*__FLT_DENORM_MIN__,
+ [6] = FLT_MIN + 3*__FLT_DENORM_MIN__,
+ };
+
+ checkf(x, __arraycount(x));
+#else /* !__FLT_HAS_DENORM__ */
+ atf_tc_skip("no subnormals on this architecture");
+#endif /* !__FLT_HAS_DENORM__ */
+}
+
+ATF_TC(nextf_near_1);
+ATF_TC_HEAD(nextf_near_1, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "nextafterf/nexttowardf near 1");
+}
+ATF_TC_BODY(nextf_near_1, tc)
+{
+ static const float x[] = {
+ [0] = 1 - 3*FLT_EPSILON/2,
+ [1] = 1 - 2*FLT_EPSILON/2,
+ [2] = 1 - FLT_EPSILON/2,
+ [3] = 1,
+ [4] = 1 + FLT_EPSILON,
+ [5] = 1 + 2*FLT_EPSILON,
+ [6] = 1 + 3*FLT_EPSILON,
+ };
+
+ checkf(x, __arraycount(x));
+}
+
+ATF_TC(nextf_near_1_5);
+ATF_TC_HEAD(nextf_near_1_5, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "nextafterf/nexttowardf near 1.5");
+}
+ATF_TC_BODY(nextf_near_1_5, tc)
+{
+ static const float x[] = {
+ [0] = 1.5 - 3*FLT_EPSILON,
+ [1] = 1.5 - 2*FLT_EPSILON,
+ [2] = 1.5 - FLT_EPSILON,
+ [3] = 1.5,
+ [4] = 1.5 + FLT_EPSILON,
+ [5] = 1.5 + 2*FLT_EPSILON,
+ [6] = 1.5 + 3*FLT_EPSILON,
+ };
+
+ checkf(x, __arraycount(x));
+}
+
+ATF_TC(nextf_near_infinity);
+ATF_TC_HEAD(nextf_near_infinity, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "nextafterf/nexttowardf near infinity");
+}
+ATF_TC_BODY(nextf_near_infinity, tc)
+{
+ static const float x[] = {
+ [0] = FLT_MAX,
+ [1] = INFINITY,
+ };
+ volatile float t;
+
+ if (!isinf(INFINITY))
+ atf_tc_skip("no infinities on this architecture");
+
+ checkf(x, __arraycount(x));
+
+ ATF_CHECK_EQ_MSG((t = nextafterf(INFINITY, INFINITY)), INFINITY,
+ "t=%a=%g", t, t);
+ ATF_CHECK_EQ_MSG((t = nextafterf(-INFINITY, -INFINITY)), -INFINITY,
+ "t=%a=%g", t, t);
+}
+
+ATF_TC(nextl_nan);
+ATF_TC_HEAD(nextl_nan, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "nextafterl/nexttowardl on NaN");
+}
+ATF_TC_BODY(nextl_nan, tc)
+{
+#ifdef NAN
+ /* XXX verify the NaN is quiet */
+ ATF_CHECK(isnan(nextafterl(NAN, 0)));
+ ATF_CHECK(isnan(nexttowardl(NAN, 0)));
+ ATF_CHECK(isnan(nextafterl(0, NAN)));
+ ATF_CHECK(isnan(nexttowardl(0, NAN)));
+#else
+ atf_tc_skip("no NaNs on this architecture");
+#endif
+}
+
+ATF_TC(nextl_signed_0);
+ATF_TC_HEAD(nextl_signed_0, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "nextafterl/nexttowardl on signed 0");
+}
+ATF_TC_BODY(nextl_signed_0, tc)
+{
+ volatile long double z_pos = +0.;
+ volatile long double z_neg = -0.;
+#if __LDBL_HAS_DENORM__
+ volatile long double m = __LDBL_DENORM_MIN__;
+#else
+ volatile long double m = LDBL_MIN;
+#endif
+
+ if (signbit(z_pos) == signbit(z_neg))
+ atf_tc_skip("no signed zeroes on this architecture");
+
+ /*
+ * `nextUp(x) is the least floating-point number in the format
+ * of x that compares greater than x. [...] nextDown(x) is
+ * -nextUp(-x).'
+ * --IEEE 754-2019, 5.3.1 General operations, p. 19
+ *
+ * Verify that nextafterl and nexttowardl, which implement the
+ * nextUp and nextDown operations, obey this rule and don't
+ * send -0 to +0 or +0 to -0, respectively.
+ */
+
+ CHECK(0, nextafterl, z_neg, +INFINITY, m);
+ CHECK(1, nexttowardl, z_neg, +INFINITY, m);
+ CHECK(2, nextafterl, z_pos, +INFINITY, m);
+ CHECK(3, nexttowardl, z_pos, +INFINITY, m);
+
+ CHECK(4, nextafterl, z_pos, -INFINITY, -m);
+ CHECK(5, nexttowardl, z_pos, -INFINITY, -m);
+ CHECK(6, nextafterl, z_neg, -INFINITY, -m);
+ CHECK(7, nexttowardl, z_neg, -INFINITY, -m);
+
+ /*
+ * `If x is the negative number of least magnitude in x's
+ * format, nextUp(x) is -0.'
+ * --IEEE 754-2019, 5.3.1 General operations, p. 19
+ */
+ CHECK(8, nextafterl, -m, +INFINITY, 0);
+ CHECK(9, nexttowardl, -m, +INFINITY, 0);
+ ATF_CHECK(signbit(nextafterl(-m, +INFINITY)) != 0);
+ CHECK(10, nextafterl, m, -INFINITY, 0);
+ CHECK(11, nexttowardl, m, -INFINITY, 0);
+ ATF_CHECK(signbit(nextafterl(m, -INFINITY)) == 0);
+}
+
+ATF_TC(nextl_near_0);
+ATF_TC_HEAD(nextl_near_0, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "nextafterl/nexttowardl near 0");
+}
+ATF_TC_BODY(nextl_near_0, tc)
+{
+ static const long double x[] = {
+ [0] = 0,
+#if __LDBL_HAS_DENORM__
+ [1] = __LDBL_DENORM_MIN__,
+ [2] = 2*__LDBL_DENORM_MIN__,
+ [3] = 3*__LDBL_DENORM_MIN__,
+ [4] = 4*__LDBL_DENORM_MIN__,
+#else
+ [1] = LDBL_MIN,
+ [2] = LDBL_MIN*(1 + LDBL_EPSILON),
+ [3] = LDBL_MIN*(1 + 2*LDBL_EPSILON),
+ [4] = LDBL_MIN*(1 + 3*LDBL_EPSILON),
+#endif
+ };
+
+ checkl(x, __arraycount(x));
+}
+
+ATF_TC(nextl_near_sub_normal);
+ATF_TC_HEAD(nextl_near_sub_normal, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "nextafterl/nexttowardl near the subnormal/normal boundary");
+}
+ATF_TC_BODY(nextl_near_sub_normal, tc)
+{
+#if __LDBL_HAS_DENORM__
+ static const long double x[] = {
+ [0] = LDBL_MIN - 3*__LDBL_DENORM_MIN__,
+ [1] = LDBL_MIN - 2*__LDBL_DENORM_MIN__,
+ [2] = LDBL_MIN - __LDBL_DENORM_MIN__,
+ [3] = LDBL_MIN,
+ [4] = LDBL_MIN + __LDBL_DENORM_MIN__,
+ [5] = LDBL_MIN + 2*__LDBL_DENORM_MIN__,
+ [6] = LDBL_MIN + 3*__LDBL_DENORM_MIN__,
+ };
+
+ checkl(x, __arraycount(x));
+#else /* !__LDBL_HAS_DENORM__ */
+ atf_tc_skip("no subnormals on this architecture");
+#endif /* !__LDBL_HAS_DENORM__ */
+}
+
+ATF_TC(nextl_near_1);
+ATF_TC_HEAD(nextl_near_1, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "nextafterl/nexttowardl near 1");
+}
+ATF_TC_BODY(nextl_near_1, tc)
+{
+ static const long double x[] = {
+ [0] = 1 - 3*LDBL_EPSILON/2,
+ [1] = 1 - 2*LDBL_EPSILON/2,
+ [2] = 1 - LDBL_EPSILON/2,
+ [3] = 1,
+ [4] = 1 + LDBL_EPSILON,
+ [5] = 1 + 2*LDBL_EPSILON,
+ [6] = 1 + 3*LDBL_EPSILON,
+ };
+
+ checkl(x, __arraycount(x));
+}
+
+ATF_TC(nextl_near_1_5);
+ATF_TC_HEAD(nextl_near_1_5, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "nextafterl/nexttowardl near 1.5");
+}
+ATF_TC_BODY(nextl_near_1_5, tc)
+{
+ static const long double x[] = {
+ [0] = 1.5 - 3*LDBL_EPSILON,
+ [1] = 1.5 - 2*LDBL_EPSILON,
+ [2] = 1.5 - LDBL_EPSILON,
+ [3] = 1.5,
+ [4] = 1.5 + LDBL_EPSILON,
+ [5] = 1.5 + 2*LDBL_EPSILON,
+ [6] = 1.5 + 3*LDBL_EPSILON,
+ };
+
+ checkl(x, __arraycount(x));
+}
+
+ATF_TC(nextl_near_infinity);
+ATF_TC_HEAD(nextl_near_infinity, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "nextafterl/nexttowardl near infinity");
+}
+ATF_TC_BODY(nextl_near_infinity, tc)
+{
+ static const long double x[] = {
+ [0] = LDBL_MAX,
+ [1] = INFINITY,
+ };
+ volatile long double t;
+
+ if (!isinf(INFINITY))
+ atf_tc_skip("no infinities on this architecture");
+
+ checkl(x, __arraycount(x));
+
+ ATF_CHECK_EQ_MSG((t = nextafterl(INFINITY, INFINITY)), INFINITY,
+ "t=%La=%Lg", t, t);
+ ATF_CHECK_EQ_MSG((t = nextafterl(-INFINITY, -INFINITY)), -INFINITY,
+ "t=%La=%Lg", t, t);
+}
+
+#endif /* __vax__ */
+
+ATF_TP_ADD_TCS(tp)
+{
+
+#ifdef __vax__
+ ATF_TP_ADD_TC(tp, vaxafter);
+#else
+ ATF_TP_ADD_TC(tp, next_nan);
+ ATF_TP_ADD_TC(tp, next_near_0);
+ ATF_TP_ADD_TC(tp, next_near_1);
+ ATF_TP_ADD_TC(tp, next_near_1_5);
+ ATF_TP_ADD_TC(tp, next_near_infinity);
+ ATF_TP_ADD_TC(tp, next_near_sub_normal);
+ ATF_TP_ADD_TC(tp, next_signed_0);
+ ATF_TP_ADD_TC(tp, nextf_nan);
+ ATF_TP_ADD_TC(tp, nextf_near_0);
+ ATF_TP_ADD_TC(tp, nextf_near_1);
+ ATF_TP_ADD_TC(tp, nextf_near_1_5);
+ ATF_TP_ADD_TC(tp, nextf_near_infinity);
+ ATF_TP_ADD_TC(tp, nextf_near_sub_normal);
+ ATF_TP_ADD_TC(tp, nextf_signed_0);
+ ATF_TP_ADD_TC(tp, nextl_nan);
+ ATF_TP_ADD_TC(tp, nextl_near_0);
+ ATF_TP_ADD_TC(tp, nextl_near_1);
+ ATF_TP_ADD_TC(tp, nextl_near_1_5);
+ ATF_TP_ADD_TC(tp, nextl_near_infinity);
+ ATF_TP_ADD_TC(tp, nextl_near_sub_normal);
+ ATF_TP_ADD_TC(tp, nextl_signed_0);
+#endif
+ return atf_no_error();
+}
diff --git a/lib/libm/t_remquo.c b/lib/libm/t_remquo.c
new file mode 100644
index 000000000000..a60e93877335
--- /dev/null
+++ b/lib/libm/t_remquo.c
@@ -0,0 +1,126 @@
+/* $NetBSD: t_remquo.c,v 1.2 2024/09/20 22:24:51 rin Exp $ */
+
+/*-
+ * Copyright (c) 2011 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jukka Ruohonen and Greg Troxel.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (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 <atf-c.h>
+#include <float.h>
+#include <math.h>
+
+/*
+ * remquo(3)
+ */
+ATF_TC(remquo_args);
+ATF_TC_HEAD(remquo_args, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test some selected arguments");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, remquo_args);
+
+ return atf_no_error();
+}
+
+#ifdef __vax__
+
+ATF_TC_BODY(remquo_args, tc)
+{
+ atf_tc_expect_fail("PR 57881: vax libm is missing various symbols");
+ atf_tc_fail("missing remquo on vax");
+}
+
+#else /* !__vax__ */
+
+static const struct {
+ double x;
+ double y;
+ double r; /* expected */
+ int quo; /* expected */
+} args[] = {
+ { -135.0, -90.0, 45.0, 2 },
+ { -45.0, -90.0, -45.0, 8 },
+ { 45.0, -90.0, 45.0, -8 },
+ { 135.0, -90.0, -45.0, -2 },
+ { -180.0, 90.0, -0.0, -2 },
+ { -135.0, 90.0, 45.0, -2 },
+ { -90.0, 90.0, -0.0, -1 },
+ { -45.0, 90.0, -45.0, -8 },
+ { 0.0, 90.0, 0.0, 0 },
+ { 45.0, 90.0, 45.0, 8 },
+ { 90.0, 90.0, 0.0, 1 },
+ { 135.0, 90.0, -45.0, 2 },
+ { 180.0, 90.0, 0.0, 2 },
+};
+
+ATF_TC_BODY(remquo_args, tc)
+{
+ const double eps = DBL_EPSILON;
+ size_t i;
+
+ for (i = 0; i < __arraycount(args); i++) {
+ double x = args[i].x;
+ double y = args[i].y;
+ double r;
+ double r_exp = args[i].r;
+ int quo;
+ int quo_exp = args[i].quo;
+
+ bool ok = true;
+
+ r = remquo(x, y, &quo);
+
+ ok &= (fabs(r - r_exp) <= eps);
+
+ /*
+ * For now, consider 0 to have positive sign.
+ */
+ if (quo_exp < 0 && quo >= 0)
+ ok = false;
+ if (quo_exp >= 0 && quo < 0)
+ ok = false;
+
+ /*
+ * The specification requires that quo be congruent to
+ * the integer remainder modulo 2^k for some k >=3.
+ */
+ ok &= ((quo & 0x7) == (quo_exp & 0x7));
+
+ if (!ok) {
+ atf_tc_fail_nonfatal("remquo(%g, %g) "
+ "r/quo expected %g/%d != %g/%d",
+ x, y, r_exp, quo_exp, r, quo);
+ }
+ }
+}
+
+#endif /* !__vax__ */
diff --git a/lib/libpthread/cancelpoint.h b/lib/libpthread/cancelpoint.h
new file mode 100644
index 000000000000..0fa332a6042c
--- /dev/null
+++ b/lib/libpthread/cancelpoint.h
@@ -0,0 +1,133 @@
+/* $NetBSD: cancelpoint.h,v 1.1 2025/04/05 11:22:32 riastradh Exp $ */
+
+/*
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TESTS_LIB_LIBPTHREAD_CANCELPOINT_H
+#define TESTS_LIB_LIBPTHREAD_CANCELPOINT_H
+
+#include <pthread.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include "h_macros.h"
+
+extern pthread_barrier_t bar;
+extern bool cleanup_done;
+
+#if 0
+atomic_bool cancelpointreadydone;
+#endif
+
+static void
+cleanup(void *cookie)
+{
+ bool *cleanup_donep = cookie;
+
+ *cleanup_donep = true;
+}
+
+static void
+cancelpointready(void)
+{
+
+ (void)pthread_barrier_wait(&bar);
+ RL(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL));
+#if 0
+ atomic_store_release(&cancelpointreadydone, true);
+#endif
+}
+
+static void *
+thread_cancelpoint(void *cookie)
+{
+ void (*cancelpoint)(void) = cookie;
+
+ RL(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL));
+ (void)pthread_barrier_wait(&bar);
+
+ pthread_cleanup_push(&cleanup, &cleanup_done);
+ (*cancelpoint)();
+ pthread_cleanup_pop(/*execute*/0);
+
+ return NULL;
+}
+
+static void
+test_cancelpoint_before(void (*cancelpoint)(void))
+{
+ pthread_t t;
+ void *result;
+
+ RZ(pthread_barrier_init(&bar, NULL, 2));
+ RZ(pthread_create(&t, NULL, &thread_cancelpoint, cancelpoint));
+
+ (void)pthread_barrier_wait(&bar);
+ fprintf(stderr, "cancel\n");
+ RZ(pthread_cancel(t));
+ (void)pthread_barrier_wait(&bar);
+
+ alarm(1);
+ RZ(pthread_join(t, &result));
+ ATF_CHECK_MSG(result == PTHREAD_CANCELED,
+ "result=%p PTHREAD_CANCELED=%p", result, PTHREAD_CANCELED);
+ ATF_CHECK(cleanup_done);
+}
+
+#if 0
+static void
+test_cancelpoint_wakeup(void (*cancelpoint)(void))
+{
+ pthread_t t;
+
+ RZ(pthread_barrier_init(&bar, NULL, 2));
+ RZ(pthread_create(&t, NULL, &cancelpoint_thread, cancelpoint));
+
+ (void)pthread_barrier_wait(&bar);
+ while (!atomic_load_acquire(&cancelpointreadydone))
+ continue;
+ while (!pthread_sleeping(t)) /* XXX find a way to do this */
+ continue;
+ RZ(pthread_cancel(t));
+}
+#endif
+
+#define TEST_CANCELPOINT(CANCELPOINT, XFAIL) \
+ATF_TC(CANCELPOINT); \
+ATF_TC_HEAD(CANCELPOINT, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", "Test cancellation point: " \
+ #CANCELPOINT); \
+} \
+ATF_TC_BODY(CANCELPOINT, tc) \
+{ \
+ XFAIL; \
+ test_cancelpoint_before(&CANCELPOINT); \
+}
+#define ADD_TEST_CANCELPOINT(CANCELPOINT) \
+ ATF_TP_ADD_TC(tp, CANCELPOINT)
+
+#endif /* TESTS_LIB_LIBPTHREAD_CANCELPOINT_H */
diff --git a/lib/libpthread/t_cancellation.c b/lib/libpthread/t_cancellation.c
new file mode 100644
index 000000000000..deb4ebb5efae
--- /dev/null
+++ b/lib/libpthread/t_cancellation.c
@@ -0,0 +1,1543 @@
+/* $NetBSD: t_cancellation.c,v 1.4 2025/04/05 11:22:32 riastradh Exp $ */
+
+/*
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_cancellation.c,v 1.4 2025/04/05 11:22:32 riastradh Exp $");
+
+#include <sys/event.h>
+#include <sys/mman.h>
+#include <sys/msg.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include <aio.h>
+#include <atf-c.h>
+#include <fcntl.h>
+#include <mqueue.h>
+#include <paths.h>
+#include <poll.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdatomic.h>
+#include <string.h>
+#include <termios.h>
+#include <threads.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "cancelpoint.h"
+#include "h_macros.h"
+
+static const char *
+c11thrd_err(int error)
+{
+ static char buf[32];
+
+ switch (error) {
+ case thrd_busy: return "thrd_busy";
+ case thrd_nomem: return "thrd_nomem";
+ case thrd_success: return "thrd_success";
+ case thrd_timedout: return "thrd_timedout";
+ default:
+ snprintf(buf, sizeof(buf), "thrd_%d", error);
+ return buf;
+ }
+}
+
+#define RT(x) do \
+{ \
+ int RT_rv = (x); \
+ ATF_REQUIRE_MSG(RT_rv == 0, "%s: %d (%s)", \
+ #x, RT_rv, c11thrd_err(RT_rv)); \
+} while (0)
+
+pthread_barrier_t bar;
+bool cleanup_done;
+
+/* POSIX style */
+static void *
+emptythread(void *cookie)
+{
+ return NULL;
+}
+
+/* C11 style */
+static int
+emptythrd(void *cookie)
+{
+ return 123;
+}
+
+static void
+cleanup_pthread_join(void *cookie)
+{
+ pthread_t *tp = cookie;
+ void *result;
+
+ RZ(pthread_join(*tp, &result));
+ ATF_CHECK_MSG(result == NULL, "result=%p", result);
+}
+
+static void
+cleanup_thrd_join(void *cookie)
+{
+ thrd_t *tp = cookie;
+ int result;
+
+ RT(thrd_join(*tp, &result));
+ ATF_CHECK_MSG(result == 123, "result=%d", result);
+}
+
+static void
+cleanup_msgid(void *cookie)
+{
+ int *msgidp = cookie;
+
+ /*
+ * These message queue identifiers are persistent, so make sure
+ * to clean them up; otherwise the operator will have to run
+ * `ipcrm -q all' from time to time or else the tests will fail
+ * with ENOSPC.
+ */
+ RL(msgctl(*msgidp, IPC_RMID, NULL));
+}
+
+/*
+ * List of cancellation points in POSIX:
+ *
+ * https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/V2_chap02.html#tag_16_09_05_02
+ */
+
+static int
+acceptsetup(void)
+{
+ struct sockaddr_un sun = { .sun_family = AF_LOCAL };
+ int sock;
+
+ strncpy(sun.sun_path, "sock", sizeof(sun.sun_path));
+ RL(sock = socket(PF_LOCAL, SOCK_STREAM, 0));
+ RL(bind(sock, (const struct sockaddr *)&sun, sizeof(sun)));
+ RL(listen(sock, 1));
+
+ return sock;
+}
+
+static void
+cancelpoint_accept(void)
+{
+ const int sock = acceptsetup();
+
+ cancelpointready();
+ RL(accept(sock, NULL, NULL));
+}
+
+static void
+cancelpoint_accept4(void)
+{
+ const int sock = acceptsetup();
+
+ cancelpointready();
+ RL(accept4(sock, NULL, NULL, O_CLOEXEC));
+}
+
+static void
+cancelpoint_aio_suspend(void)
+{
+ int fd[2];
+ char buf[32];
+ struct aiocb aio = {
+ .aio_offset = 0,
+ .aio_buf = buf,
+ .aio_nbytes = sizeof(buf),
+ .aio_fildes = -1,
+ };
+ const struct aiocb *const aiolist[] = { &aio };
+
+ RL(pipe(fd));
+ aio.aio_fildes = fd[0];
+ RL(aio_read(&aio));
+ cancelpointready();
+ RL(aio_suspend(aiolist, __arraycount(aiolist), NULL));
+}
+
+static void
+cancelpoint_clock_nanosleep(void)
+{
+ /* XXX test all CLOCK_*? */
+ struct timespec t = {.tv_sec = 1, .tv_nsec = 0};
+
+ cancelpointready();
+ RL(clock_nanosleep(CLOCK_MONOTONIC, 0, &t, NULL));
+}
+
+static void
+cancelpoint_close(void)
+{
+ int fd;
+
+ RL(fd = open("/dev/null", O_RDWR));
+ cancelpointready();
+ RL(close(fd));
+}
+
+static void
+cancelpoint_cnd_timedwait(void)
+{
+ cnd_t cnd;
+ mtx_t mtx;
+ struct timespec t = {.tv_sec = 1, .tv_nsec = 0};
+
+ RT(cnd_init(&cnd));
+ RT(mtx_init(&mtx, mtx_plain));
+ cancelpointready();
+ RT(mtx_lock(&mtx));
+ RT(cnd_timedwait(&cnd, &mtx, &t));
+ RT(mtx_unlock(&mtx));
+}
+
+static void
+cancelpoint_cnd_wait(void)
+{
+ cnd_t cnd;
+ mtx_t mtx;
+
+ RT(cnd_init(&cnd));
+ RT(mtx_init(&mtx, mtx_plain));
+ cancelpointready();
+ RT(mtx_lock(&mtx));
+ RT(cnd_wait(&cnd, &mtx));
+ RT(mtx_unlock(&mtx));
+}
+
+static void
+cancelpoint_connect(void)
+{
+ struct sockaddr_un sun = { .sun_family = AF_LOCAL };
+ int sock;
+
+ strncpy(sun.sun_path, "sock", sizeof(sun.sun_path));
+ RL(sock = socket(PF_LOCAL, SOCK_STREAM, 0));
+ cancelpointready();
+ RL(connect(sock, (const struct sockaddr *)&sun, sizeof(sun)));
+}
+
+static void
+cancelpoint_creat(void)
+{
+
+ cancelpointready();
+ RL(creat("file", 0666));
+}
+
+static void
+cancelpoint_fcntl_F_SETLKW(void)
+{
+ int fd;
+ struct flock fl = {
+ .l_start = 0,
+ .l_len = 0,
+ .l_type = F_WRLCK,
+ .l_whence = SEEK_SET,
+ };
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0666));
+ cancelpointready();
+ RL(fcntl(fd, F_SETLKW, &fl));
+}
+
+static void
+cancelpoint_fcntl_F_OFD_SETLKW(void)
+{
+#ifdef F_OFD_SETLKW
+ int fd;
+ struct flock fl = {
+ .l_start = 0,
+ .l_len = 0,
+ .l_type = F_WRLCK,
+ .l_whence = SEEK_SET,
+ };
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0666));
+ cancelpointready();
+ RL(fcntl(fd, F_OFD_SETLKW, &fl));
+#else
+ atf_tc_expect_fail("PR kern/59241: POSIX.1-2024:"
+ " OFD-owned file locks");
+ atf_tc_fail("no F_OFD_SETLKW");
+#endif
+}
+
+static void
+cancelpoint_fdatasync(void)
+{
+ int fd;
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0666));
+ cancelpointready();
+ RL(fdatasync(fd));
+}
+
+static void
+cancelpoint_fsync(void)
+{
+ int fd;
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0666));
+ cancelpointready();
+ RL(fsync(fd));
+}
+
+static void
+cancelpoint_kevent(void)
+{
+ int kq;
+ struct kevent ev;
+
+ EV_SET(&ev, SIGUSR1, EVFILT_SIGNAL, EV_ADD|EV_ENABLE,
+ /*fflags*/0, /*data*/0, /*udata*/0);
+
+ RL(kq = kqueue());
+ RL(kevent(kq, &ev, 1, NULL, 1, &(const struct timespec){0,0}));
+ cancelpointready();
+ RL(kevent(kq, NULL, 0, &ev, 1, NULL));
+}
+
+static void
+cancelpoint_lockf_F_LOCK(void)
+{
+ int fd;
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0666));
+ cancelpointready();
+ RL(lockf(fd, F_LOCK, 0));
+}
+
+static void
+cancelpoint_mq_receive(void)
+{
+ mqd_t mq;
+ char buf[32];
+
+ RL(mq = mq_open("mq", O_RDWR|O_CREAT, 0666, NULL));
+ cancelpointready();
+ RL(mq_receive(mq, buf, sizeof(buf), NULL));
+}
+
+static void
+cancelpoint_mq_send(void)
+{
+ mqd_t mq;
+ char buf[32] = {0};
+
+ RL(mq = mq_open("mq", O_RDWR|O_CREAT, 0666, NULL));
+ cancelpointready();
+ RL(mq_send(mq, buf, sizeof(buf), 0));
+}
+
+static void
+cancelpoint_mq_timedreceive(void)
+{
+ mqd_t mq;
+ char buf[32];
+ struct timespec t = {.tv_sec = 1, .tv_nsec = 0};
+
+ RL(mq = mq_open("mq", O_RDWR|O_CREAT, 0666, NULL));
+ cancelpointready();
+ RL(mq_timedreceive(mq, buf, sizeof(buf), NULL, &t));
+}
+
+static void
+cancelpoint_mq_timedsend(void)
+{
+ mqd_t mq;
+ char buf[32] = {0};
+ struct timespec t = {.tv_sec = 1, .tv_nsec = 0};
+
+ RL(mq = mq_open("mq", O_RDWR|O_CREAT, 0666, NULL));
+ cancelpointready();
+ RL(mq_timedsend(mq, buf, sizeof(buf), 0, &t));
+}
+
+static void
+cancelpoint_msgrcv(void)
+{
+ int msgid;
+ char buf[32];
+
+ RL(msgid = msgget(IPC_PRIVATE, IPC_CREAT));
+ pthread_cleanup_push(&cleanup_msgid, &msgid);
+ cancelpointready();
+ RL(msgrcv(msgid, buf, sizeof(buf), 0, 0));
+ pthread_cleanup_pop(/*execute*/1);
+}
+
+static void
+cancelpoint_msgsnd(void)
+{
+ int msgid;
+ char buf[32] = {0};
+
+ RL(msgid = msgget(IPC_PRIVATE, IPC_CREAT));
+ pthread_cleanup_push(&cleanup_msgid, &msgid);
+ cancelpointready();
+ RL(msgsnd(msgid, buf, sizeof(buf), 0));
+ pthread_cleanup_pop(/*execute*/1);
+}
+
+static void
+cancelpoint_msync(void)
+{
+ const unsigned long pagesize = sysconf(_SC_PAGESIZE);
+ int fd;
+ void *map;
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0666));
+ RL(ftruncate(fd, pagesize));
+ REQUIRE_LIBC(map = mmap(NULL, pagesize, PROT_READ|PROT_WRITE,
+ MAP_SHARED, fd, 0),
+ MAP_FAILED);
+ cancelpointready();
+ RL(msync(map, pagesize, MS_SYNC));
+}
+
+static void
+cancelpoint_nanosleep(void)
+{
+ /* XXX test all CLOCK_*? */
+ struct timespec t = {.tv_sec = 1, .tv_nsec = 0};
+
+ cancelpointready();
+ RL(nanosleep(&t, NULL));
+}
+
+static void
+cancelpoint_open(void)
+{
+
+ cancelpointready();
+ RL(open("file", O_RDWR));
+}
+
+static void
+cancelpoint_openat(void)
+{
+
+ cancelpointready();
+ RL(openat(AT_FDCWD, "file", O_RDWR));
+}
+
+static void
+cancelpoint_pause(void)
+{
+
+ cancelpointready();
+ RL(pause());
+}
+
+static void
+cancelpoint_poll(void)
+{
+ int fd[2];
+ struct pollfd pfd;
+
+ RL(pipe(fd));
+ pfd.fd = fd[0];
+ pfd.events = POLLIN;
+ cancelpointready();
+ RL(poll(&pfd, 1, 1000));
+}
+
+static void
+cancelpoint_posix_close(void)
+{
+#if 0
+ int fd;
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0666));
+ cancelpointready();
+ RL(posix_close(fd, POSIX_CLOSE_RESTART));
+#else
+ atf_tc_expect_fail("PR kern/58929: POSIX.1-2024 compliance:"
+ " posix_close, POSIX_CLOSE_RESTART");
+ atf_tc_fail("no posix_close");
+#endif
+}
+
+static void
+cancelpoint_ppoll(void)
+{
+ int fd[2];
+ struct pollfd pfd;
+ struct timespec t = {.tv_sec = 1, .tv_nsec = 0};
+
+ RL(pipe(fd));
+ pfd.fd = fd[0];
+ pfd.events = POLLIN;
+ cancelpointready();
+ RL(ppoll(&pfd, 1, &t, NULL));
+}
+
+static void
+cancelpoint_pread(void)
+{
+ int fd;
+ char buf[1];
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0666));
+ cancelpointready();
+ RL(pread(fd, buf, sizeof(buf), 1));
+}
+
+
+static void
+cancelpoint_pselect(void)
+{
+ int fd[2];
+ fd_set readfd;
+ struct timespec t = {.tv_sec = 1, .tv_nsec = 0};
+
+ FD_ZERO(&readfd);
+
+ RL(pipe(fd));
+ FD_SET(fd[0], &readfd);
+ cancelpointready();
+ RL(pselect(fd[0] + 1, &readfd, NULL, NULL, &t, NULL));
+}
+
+static void
+cancelpoint_pthread_cond_clockwait(void)
+{
+#if 0
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+ struct timespec t = {.tv_sec = 1, .tv_nsec = 0};
+
+ RZ(pthread_cond_init(&cond, NULL));
+ RZ(pthread_mutex_init(&mutex, NULL));
+ cancelpointready();
+ RZ(pthread_mutex_lock(&mutex));
+ RZ(pthread_cond_clockwait(&cond, &mutex, CLOCK_MONOTONIC, &t));
+ RZ(pthread_mutex_unlock(&mutex));
+#else
+ atf_tc_expect_fail("PR lib/59142: POSIX.1-2024:"
+ " pthread_cond_clockwait and company");
+ atf_tc_fail("no posix_cond_clockwait");
+#endif
+}
+
+static void
+cancelpoint_pthread_cond_timedwait(void)
+{
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+ struct timespec t = {.tv_sec = 1, .tv_nsec = 0};
+
+ RZ(pthread_cond_init(&cond, NULL));
+ RZ(pthread_mutex_init(&mutex, NULL));
+ cancelpointready();
+ RZ(pthread_mutex_lock(&mutex));
+ RZ(pthread_cond_timedwait(&cond, &mutex, &t));
+ RZ(pthread_mutex_unlock(&mutex));
+}
+
+static void
+cancelpoint_pthread_cond_wait(void)
+{
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+
+ RZ(pthread_cond_init(&cond, NULL));
+ RZ(pthread_mutex_init(&mutex, NULL));
+ cancelpointready();
+ RZ(pthread_mutex_lock(&mutex));
+ RZ(pthread_cond_wait(&cond, &mutex));
+ RZ(pthread_mutex_unlock(&mutex));
+}
+
+static void
+cancelpoint_pthread_join(void)
+{
+ pthread_t t;
+
+ RZ(pthread_create(&t, NULL, &emptythread, NULL));
+ pthread_cleanup_push(&cleanup_pthread_join, &t);
+ cancelpointready();
+ RZ(pthread_join(t, NULL));
+ pthread_cleanup_pop(/*execute*/0);
+}
+
+static void
+cancelpoint_pthread_testcancel(void)
+{
+
+ cancelpointready();
+ pthread_testcancel();
+}
+
+static void
+cancelpoint_pwrite(void)
+{
+ int fd;
+ char buf[1] = {0};
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0666));
+ cancelpointready();
+ RL(pwrite(fd, buf, sizeof(buf), 1));
+}
+
+static void
+cancelpoint_read(void)
+{
+ int fd;
+ char buf[1];
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0666));
+ cancelpointready();
+ RL(read(fd, buf, sizeof(buf)));
+}
+
+static void
+cancelpoint_readv(void)
+{
+ int fd;
+ char buf[1];
+ struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) };
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0666));
+ cancelpointready();
+ RL(readv(fd, &iov, 1));
+}
+
+static void
+cancelpoint_recv(void)
+{
+ struct sockaddr_un sun = { .sun_family = AF_LOCAL };
+ int sock;
+ char buf[1];
+
+ strncpy(sun.sun_path, "sock", sizeof(sun.sun_path));
+ RL(sock = socket(PF_LOCAL, SOCK_DGRAM, 0));
+ RL(bind(sock, (const struct sockaddr *)&sun, sizeof(sun)));
+ cancelpointready();
+ RL(recv(sock, buf, sizeof(buf), 0));
+}
+
+static void
+cancelpoint_recvfrom(void)
+{
+ struct sockaddr_un sun = { .sun_family = AF_LOCAL };
+ int sock;
+ char buf[1];
+ struct sockaddr_storage ss;
+ socklen_t len = sizeof(ss);
+
+ strncpy(sun.sun_path, "sock", sizeof(sun.sun_path));
+ RL(sock = socket(PF_LOCAL, SOCK_DGRAM, 0));
+ RL(bind(sock, (const struct sockaddr *)&sun, sizeof(sun)));
+ cancelpointready();
+ RL(recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&ss, &len));
+}
+
+static void
+cancelpoint_recvmsg(void)
+{
+ struct sockaddr_un sun = { .sun_family = AF_LOCAL };
+ int sock;
+ char buf[1];
+ struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) };
+ struct msghdr msg = {
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+
+ strncpy(sun.sun_path, "sock", sizeof(sun.sun_path));
+ RL(sock = socket(PF_LOCAL, SOCK_DGRAM, 0));
+ RL(bind(sock, (const struct sockaddr *)&sun, sizeof(sun)));
+ cancelpointready();
+ RL(recvmsg(sock, &msg, 0));
+}
+
+static void
+cancelpoint_select(void)
+{
+ int fd[2];
+ fd_set readfd;
+ struct timeval t = {.tv_sec = 1, .tv_usec = 0};
+
+ FD_ZERO(&readfd);
+
+ RL(pipe(fd));
+ FD_SET(fd[0], &readfd);
+ cancelpointready();
+ RL(select(fd[0] + 1, &readfd, NULL, NULL, &t));
+}
+
+static void
+cancelpoint_send(void)
+{
+ struct sockaddr_un sun = { .sun_family = AF_LOCAL };
+ int sock;
+ char buf[1] = {0};
+
+ strncpy(sun.sun_path, "sock", sizeof(sun.sun_path));
+ RL(sock = socket(PF_LOCAL, SOCK_DGRAM, 0));
+ RL(bind(sock, (const struct sockaddr *)&sun, sizeof(sun)));
+ cancelpointready();
+ RL(send(sock, buf, sizeof(buf), 0));
+}
+
+static void
+cancelpoint_sendto(void)
+{
+ struct sockaddr_un sun = { .sun_family = AF_LOCAL };
+ int sock;
+ char buf[1] = {0};
+
+ strncpy(sun.sun_path, "sock", sizeof(sun.sun_path));
+ RL(sock = socket(PF_LOCAL, SOCK_DGRAM, 0));
+ cancelpointready();
+ RL(sendto(sock, buf, sizeof(buf), 0, (const struct sockaddr *)&sun,
+ sizeof(sun)));
+}
+
+static void
+cancelpoint_sendmsg(void)
+{
+ struct sockaddr_un sun = { .sun_family = AF_LOCAL };
+ int sock;
+ char buf[1] = {0};
+ struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) };
+ struct msghdr msg = {
+ .msg_name = (struct sockaddr *)&sun,
+ .msg_namelen = sizeof(sun),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+
+ strncpy(sun.sun_path, "sock", sizeof(sun.sun_path));
+ RL(sock = socket(PF_LOCAL, SOCK_DGRAM, 0));
+ cancelpointready();
+ RL(sendmsg(sock, &msg, 0));
+}
+
+static void
+cancelpoint_sigsuspend(void)
+{
+ sigset_t mask, omask;
+
+ RL(sigfillset(&mask));
+ RL(sigprocmask(SIG_BLOCK, &mask, &omask));
+ cancelpointready();
+ RL(sigsuspend(&omask));
+}
+
+static void
+cancelpoint_sigtimedwait(void)
+{
+ sigset_t mask, omask;
+ siginfo_t info;
+ struct timespec t = {.tv_sec = 1, .tv_nsec = 0};
+
+ RL(sigfillset(&mask));
+ RL(sigprocmask(SIG_BLOCK, &mask, &omask));
+ cancelpointready();
+ RL(sigtimedwait(&omask, &info, &t));
+}
+
+static void
+cancelpoint_sigwait(void)
+{
+ sigset_t mask, omask;
+ int sig;
+
+ RL(sigfillset(&mask));
+ RL(sigprocmask(SIG_BLOCK, &mask, &omask));
+ cancelpointready();
+ RL(sigwait(&omask, &sig));
+}
+
+static void
+cancelpoint_sigwaitinfo(void)
+{
+ sigset_t mask, omask;
+ siginfo_t info;
+
+ RL(sigfillset(&mask));
+ RL(sigprocmask(SIG_BLOCK, &mask, &omask));
+ cancelpointready();
+ RL(sigwaitinfo(&omask, &info));
+}
+
+static void
+cancelpoint_sleep(void)
+{
+
+ cancelpointready();
+ (void)sleep(1);
+}
+
+static void
+cancelpoint_tcdrain(void)
+{
+ int hostfd, appfd;
+ char *pts;
+
+ RL(hostfd = posix_openpt(O_RDWR|O_NOCTTY));
+ RL(grantpt(hostfd));
+ RL(unlockpt(hostfd));
+ REQUIRE_LIBC(pts = ptsname(hostfd), NULL);
+ RL(appfd = open(pts, O_RDWR|O_NOCTTY));
+ cancelpointready();
+ RL(tcdrain(appfd));
+}
+
+static void
+cancelpoint_thrd_join(void)
+{
+ thrd_t t;
+
+ RT(thrd_create(&t, &emptythrd, NULL));
+ pthread_cleanup_push(&cleanup_thrd_join, &t);
+ cancelpointready();
+ RT(thrd_join(t, NULL));
+ pthread_cleanup_pop(/*execute*/0);
+}
+
+static void
+cancelpoint_thrd_sleep(void)
+{
+ struct timespec t = {.tv_sec = 1, .tv_nsec = 0};
+
+ cancelpointready();
+ RT(thrd_sleep(&t, NULL));
+}
+
+static void
+cancelpoint_wait(void)
+{
+
+ cancelpointready();
+ RL(wait(NULL));
+}
+
+static void
+cancelpoint_waitid(void)
+{
+
+ cancelpointready();
+ RL(waitid(P_ALL, 0, NULL, 0));
+}
+
+static void
+cancelpoint_waitpid(void)
+{
+
+ cancelpointready();
+ RL(waitpid(-1, NULL, 0));
+}
+
+static void
+cancelpoint_write(void)
+{
+ int fd;
+ char buf[1] = {0};
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0666));
+ cancelpointready();
+ RL(write(fd, buf, sizeof(buf)));
+}
+
+static void
+cancelpoint_writev(void)
+{
+ int fd;
+ char buf[1] = {0};
+ struct iovec iov = { .iov_base = buf, .iov_len = sizeof(buf) };
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0666));
+ cancelpointready();
+ RL(writev(fd, &iov, 1));
+}
+
+TEST_CANCELPOINT(cancelpoint_accept, __nothing)
+TEST_CANCELPOINT(cancelpoint_accept4, __nothing)
+TEST_CANCELPOINT(cancelpoint_aio_suspend, __nothing)
+TEST_CANCELPOINT(cancelpoint_clock_nanosleep, __nothing)
+TEST_CANCELPOINT(cancelpoint_close, __nothing)
+TEST_CANCELPOINT(cancelpoint_cnd_timedwait, __nothing)
+TEST_CANCELPOINT(cancelpoint_cnd_wait, __nothing)
+TEST_CANCELPOINT(cancelpoint_connect, __nothing)
+TEST_CANCELPOINT(cancelpoint_creat, __nothing)
+TEST_CANCELPOINT(cancelpoint_fcntl_F_SETLKW, __nothing)
+TEST_CANCELPOINT(cancelpoint_fcntl_F_OFD_SETLKW, __nothing)
+TEST_CANCELPOINT(cancelpoint_fdatasync, __nothing)
+TEST_CANCELPOINT(cancelpoint_fsync, __nothing)
+TEST_CANCELPOINT(cancelpoint_kevent, __nothing)
+TEST_CANCELPOINT(cancelpoint_lockf_F_LOCK, __nothing)
+TEST_CANCELPOINT(cancelpoint_mq_receive, __nothing)
+TEST_CANCELPOINT(cancelpoint_mq_send, __nothing)
+TEST_CANCELPOINT(cancelpoint_mq_timedreceive, __nothing)
+TEST_CANCELPOINT(cancelpoint_mq_timedsend, __nothing)
+TEST_CANCELPOINT(cancelpoint_msgrcv, __nothing)
+TEST_CANCELPOINT(cancelpoint_msgsnd, __nothing)
+TEST_CANCELPOINT(cancelpoint_msync, __nothing)
+TEST_CANCELPOINT(cancelpoint_nanosleep, __nothing)
+TEST_CANCELPOINT(cancelpoint_open, __nothing)
+TEST_CANCELPOINT(cancelpoint_openat, __nothing)
+TEST_CANCELPOINT(cancelpoint_pause, __nothing)
+TEST_CANCELPOINT(cancelpoint_poll, __nothing)
+TEST_CANCELPOINT(cancelpoint_posix_close, __nothing)
+TEST_CANCELPOINT(cancelpoint_ppoll, __nothing)
+TEST_CANCELPOINT(cancelpoint_pread, __nothing)
+TEST_CANCELPOINT(cancelpoint_pselect, __nothing)
+TEST_CANCELPOINT(cancelpoint_pthread_cond_clockwait, __nothing)
+TEST_CANCELPOINT(cancelpoint_pthread_cond_timedwait, __nothing)
+TEST_CANCELPOINT(cancelpoint_pthread_cond_wait, __nothing)
+TEST_CANCELPOINT(cancelpoint_pthread_join, __nothing)
+TEST_CANCELPOINT(cancelpoint_pthread_testcancel, __nothing)
+TEST_CANCELPOINT(cancelpoint_pwrite, __nothing)
+TEST_CANCELPOINT(cancelpoint_read, __nothing)
+TEST_CANCELPOINT(cancelpoint_readv, __nothing)
+TEST_CANCELPOINT(cancelpoint_recv, __nothing)
+TEST_CANCELPOINT(cancelpoint_recvfrom, __nothing)
+TEST_CANCELPOINT(cancelpoint_recvmsg, __nothing)
+TEST_CANCELPOINT(cancelpoint_select, __nothing)
+TEST_CANCELPOINT(cancelpoint_send, __nothing)
+TEST_CANCELPOINT(cancelpoint_sendto, __nothing)
+TEST_CANCELPOINT(cancelpoint_sendmsg, __nothing)
+TEST_CANCELPOINT(cancelpoint_sigsuspend, __nothing)
+TEST_CANCELPOINT(cancelpoint_sigtimedwait, __nothing)
+TEST_CANCELPOINT(cancelpoint_sigwait, __nothing)
+TEST_CANCELPOINT(cancelpoint_sigwaitinfo, __nothing)
+TEST_CANCELPOINT(cancelpoint_sleep, __nothing)
+TEST_CANCELPOINT(cancelpoint_tcdrain, __nothing)
+TEST_CANCELPOINT(cancelpoint_thrd_join, __nothing)
+TEST_CANCELPOINT(cancelpoint_thrd_sleep, __nothing)
+TEST_CANCELPOINT(cancelpoint_wait, __nothing)
+TEST_CANCELPOINT(cancelpoint_waitid, __nothing)
+TEST_CANCELPOINT(cancelpoint_waitpid, __nothing)
+TEST_CANCELPOINT(cancelpoint_write, __nothing)
+TEST_CANCELPOINT(cancelpoint_writev, __nothing)
+
+ATF_TC(cleanuppop0);
+ATF_TC_HEAD(cleanuppop0, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test pthread_cleanup_pop(0)");
+}
+ATF_TC_BODY(cleanuppop0, tc)
+{
+
+ pthread_cleanup_push(&cleanup, &cleanup_done);
+ pthread_cleanup_pop(/*execute*/0);
+ ATF_CHECK(!cleanup_done);
+}
+
+ATF_TC(cleanuppop1);
+ATF_TC_HEAD(cleanuppop1, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test pthread_cleanup_pop(1)");
+}
+ATF_TC_BODY(cleanuppop1, tc)
+{
+
+ pthread_cleanup_push(&cleanup, &cleanup_done);
+ pthread_cleanup_pop(/*execute*/1);
+ ATF_CHECK(cleanup_done);
+}
+
+static void *
+cancelself_async(void *cookie)
+{
+ int *n = cookie;
+
+ RZ(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL));
+
+ pthread_cleanup_push(&cleanup, &cleanup_done);
+
+ *n = 1;
+ RZ(pthread_cancel(pthread_self())); /* cancel */
+ *n = 2;
+ RZ(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL));
+ pthread_testcancel();
+ *n = 3;
+ RZ(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL));
+ pthread_testcancel();
+ *n = 4;
+
+ pthread_cleanup_pop(/*execute*/0);
+ return NULL;
+}
+
+ATF_TC(cancelself_async);
+ATF_TC_HEAD(cancelself_async, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test pthread_cancel(pthread_self()) async");
+}
+ATF_TC_BODY(cancelself_async, tc)
+{
+ int n = 0;
+ pthread_t t;
+
+ RZ(pthread_create(&t, NULL, &cancelself_async, &n));
+
+ alarm(1);
+ RZ(pthread_join(t, NULL));
+
+ atf_tc_expect_fail("lib/59135: PTHREAD_CANCEL_ASYNCHRONOUS"
+ " doesn't do much");
+ ATF_CHECK_MSG(n == 1, "n=%d", n);
+ atf_tc_expect_pass();
+ ATF_CHECK(cleanup_done);
+}
+
+static void *
+cancelself_deferred(void *cookie)
+{
+ int *n = cookie;
+
+ /* PTHREAD_CANCEL_DEFERRED by default */
+
+ pthread_cleanup_push(&cleanup, &cleanup_done);
+
+ *n = 1;
+ RZ(pthread_cancel(pthread_self()));
+ *n = 2;
+ RZ(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL));
+ *n = 3;
+ pthread_testcancel();
+ *n = 4;
+ RZ(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL));
+ *n = 5;
+ pthread_testcancel(); /* cancel */
+ *n = 6;
+
+ pthread_cleanup_pop(/*execute*/0);
+ return NULL;
+}
+
+ATF_TC(cancelself_deferred);
+ATF_TC_HEAD(cancelself_deferred, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test pthread_cancel(pthread_self()) deferred");
+}
+ATF_TC_BODY(cancelself_deferred, tc)
+{
+ int n = 0;
+ pthread_t t;
+
+ RZ(pthread_create(&t, NULL, &cancelself_deferred, &n));
+
+ alarm(1);
+ RZ(pthread_join(t, NULL));
+
+ ATF_CHECK_MSG(n == 5, "n=%d", n);
+ ATF_CHECK(cleanup_done);
+}
+
+static void *
+defaults(void *cookie)
+{
+ int state, type;
+
+ fprintf(stderr, "created thread\n");
+
+ RZ(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &state));
+ RZ(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &type));
+
+ ATF_CHECK_MSG(state == PTHREAD_CANCEL_ENABLE,
+ "state=%d PTHREAD_CANCEL_ENABLE=%d PTHREAD_CANCEL_DISABLE=%d",
+ state, PTHREAD_CANCEL_ENABLE, PTHREAD_CANCEL_DISABLE);
+
+ ATF_CHECK_MSG(type == PTHREAD_CANCEL_DEFERRED,
+ "type=%d"
+ " PTHREAD_CANCEL_DEFERRED=%d PTHREAD_CANCEL_ASYNCHRONOUS=%d",
+ type, PTHREAD_CANCEL_DEFERRED, PTHREAD_CANCEL_ASYNCHRONOUS);
+
+ return NULL;
+}
+
+ATF_TC(defaults);
+ATF_TC_HEAD(defaults, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test default cancelability");
+}
+ATF_TC_BODY(defaults, tc)
+{
+ pthread_t t;
+
+ fprintf(stderr, "initial thread\n");
+ (void)defaults(NULL);
+
+ RZ(pthread_create(&t, NULL, &defaults, NULL));
+
+ alarm(1);
+ RZ(pthread_join(t, NULL));
+}
+
+static void *
+disable_enable(void *cookie)
+{
+ int *n = cookie;
+
+ pthread_cleanup_push(&cleanup, &cleanup_done);
+
+ *n = 1;
+ pthread_testcancel();
+ *n = 2;
+ RZ(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL));
+ *n = 3;
+ (void)pthread_barrier_wait(&bar);
+ *n = 4;
+ pthread_testcancel();
+ *n = 5;
+ (void)pthread_barrier_wait(&bar);
+ *n = 6;
+ pthread_testcancel();
+ *n = 7;
+ RZ(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL));
+ *n = 8;
+ pthread_testcancel(); /* cancel */
+ *n = 9;
+
+ pthread_cleanup_pop(/*execute*/0);
+ return NULL;
+}
+
+ATF_TC(disable_enable);
+ATF_TC_HEAD(disable_enable, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test disabling and re-enabling cancellation");
+}
+ATF_TC_BODY(disable_enable, tc)
+{
+ int n = 0;
+ pthread_t t;
+
+ RZ(pthread_barrier_init(&bar, NULL, 2));
+
+ RZ(pthread_create(&t, NULL, &disable_enable, &n));
+
+ (void)pthread_barrier_wait(&bar);
+ RZ(pthread_cancel(t));
+ (void)pthread_barrier_wait(&bar);
+
+ alarm(1);
+ RZ(pthread_join(t, NULL));
+
+ ATF_CHECK_MSG(n == 8, "n=%d", n);
+ ATF_CHECK(cleanup_done);
+}
+
+static void *
+notestcancel_loop_async(void *cookie)
+{
+
+ RZ(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL));
+
+ pthread_cleanup_push(&cleanup, &cleanup_done);
+ (void)pthread_barrier_wait(&bar);
+ for (;;)
+ __insn_barrier();
+ pthread_cleanup_pop(/*execute*/0);
+
+ return NULL;
+}
+
+ATF_TC(notestcancel_loop_async);
+ATF_TC_HEAD(notestcancel_loop_async, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test nothing in a loop with PTHREAD_CANCEL_ASYNCHRONOUS");
+}
+ATF_TC_BODY(notestcancel_loop_async, tc)
+{
+ pthread_t t;
+ void *result;
+
+ RZ(pthread_barrier_init(&bar, NULL, 2));
+ RZ(pthread_create(&t, NULL, &notestcancel_loop_async, NULL));
+
+ (void)pthread_barrier_wait(&bar);
+ RZ(pthread_cancel(t));
+
+ atf_tc_expect_signal(SIGALRM, "lib/59135: PTHREAD_CANCEL_ASYNCHRONOUS"
+ " doesn't do much");
+ alarm(1);
+ RZ(pthread_join(t, &result));
+ ATF_CHECK_MSG(result == PTHREAD_CANCELED,
+ "result=%p PTHREAD_CANCELED=%p", result, PTHREAD_CANCELED);
+ ATF_CHECK(cleanup_done);
+}
+
+static void *
+disable_enable_async(void *cookie)
+{
+ int *n = cookie;
+
+ pthread_cleanup_push(&cleanup, &cleanup_done);
+
+ RZ(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL));
+
+ *n = 1;
+ pthread_testcancel();
+ *n = 2;
+ RZ(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL));
+ *n = 3;
+ (void)pthread_barrier_wait(&bar);
+ *n = 4;
+ pthread_testcancel();
+ *n = 5;
+ (void)pthread_barrier_wait(&bar);
+ *n = 6;
+ pthread_testcancel();
+ *n = 7;
+ RZ(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL)); /* cancel */
+ *n = 8;
+ pthread_testcancel();
+ *n = 9;
+
+ pthread_cleanup_pop(/*execute*/0);
+ return NULL;
+}
+
+ATF_TC(disable_enable_async);
+ATF_TC_HEAD(disable_enable_async, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test disabling and re-enabling cancellation when asynchronous");
+}
+ATF_TC_BODY(disable_enable_async, tc)
+{
+ int n = 0;
+ pthread_t t;
+
+ RZ(pthread_barrier_init(&bar, NULL, 2));
+
+ RZ(pthread_create(&t, NULL, &disable_enable_async, &n));
+
+ (void)pthread_barrier_wait(&bar);
+ RZ(pthread_cancel(t));
+ (void)pthread_barrier_wait(&bar);
+
+ alarm(1);
+ RZ(pthread_join(t, NULL));
+
+ ATF_CHECK_MSG(n == 7, "n=%d", n);
+ ATF_CHECK(cleanup_done);
+}
+
+static void *
+disable_enable_setcanceltype_async(void *cookie)
+{
+ int *n = cookie;
+
+ pthread_cleanup_push(&cleanup, &cleanup_done);
+
+ *n = 1;
+ pthread_testcancel();
+ *n = 2;
+ RZ(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL));
+ *n = 3;
+ (void)pthread_barrier_wait(&bar);
+ *n = 4;
+ pthread_testcancel();
+ *n = 5;
+ (void)pthread_barrier_wait(&bar);
+ *n = 6;
+ pthread_testcancel();
+ *n = 7;
+ RZ(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL));
+ *n = 8;
+ pthread_testcancel();
+ *n = 9;
+ RZ(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL)); /* cancel */
+ *n = 10;
+ pthread_testcancel();
+ *n = 11;
+
+ pthread_cleanup_pop(/*execute*/0);
+ return NULL;
+}
+
+ATF_TC(disable_enable_setcanceltype_async);
+ATF_TC_HEAD(disable_enable_setcanceltype_async, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test disabling cancellation, setting it async, and re-enabling");
+}
+ATF_TC_BODY(disable_enable_setcanceltype_async, tc)
+{
+ int n = 0;
+ pthread_t t;
+
+ RZ(pthread_barrier_init(&bar, NULL, 2));
+
+ RZ(pthread_create(&t, NULL, &disable_enable_setcanceltype_async, &n));
+
+ (void)pthread_barrier_wait(&bar);
+ RZ(pthread_cancel(t));
+ (void)pthread_barrier_wait(&bar);
+
+ alarm(1);
+ RZ(pthread_join(t, NULL));
+
+ ATF_CHECK_MSG(n == 9, "n=%d", n);
+ ATF_CHECK(cleanup_done);
+}
+
+static void *
+setcanceltype_async(void *cookie)
+{
+ int *n = cookie;
+
+ pthread_cleanup_push(&cleanup, &cleanup_done);
+
+ *n = 1;
+ pthread_testcancel();
+ *n = 2;
+ (void)pthread_barrier_wait(&bar);
+ *n = 3;
+ (void)pthread_barrier_wait(&bar);
+ *n = 4;
+ RZ(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,
+ NULL)); /* cancel */
+ *n = 5;
+
+ pthread_cleanup_pop(/*execute*/0);
+ return NULL;
+}
+
+ATF_TC(setcanceltype_async);
+ATF_TC_HEAD(setcanceltype_async, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test disabling cancellation, setting it async, and re-enabling");
+}
+ATF_TC_BODY(setcanceltype_async, tc)
+{
+ int n = 0;
+ pthread_t t;
+
+ RZ(pthread_barrier_init(&bar, NULL, 2));
+
+ RZ(pthread_create(&t, NULL, &setcanceltype_async, &n));
+
+ (void)pthread_barrier_wait(&bar);
+ RZ(pthread_cancel(t));
+ (void)pthread_barrier_wait(&bar);
+
+ alarm(1);
+ RZ(pthread_join(t, NULL));
+
+ ATF_CHECK_MSG(n == 4, "n=%d", n);
+ ATF_CHECK(cleanup_done);
+}
+
+static void
+sighandler(int signo)
+{
+ int state;
+
+ RZ(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state));
+ RZ(pthread_setcancelstate(state, NULL));
+}
+
+static void *
+sigsafecancelstate(void *cookie)
+{
+ atomic_ulong *n = cookie;
+ char name[128];
+
+ pthread_cleanup_push(&cleanup, &cleanup_done);
+ REQUIRE_LIBC(signal(SIGUSR1, &sighandler), SIG_ERR);
+
+ (void)pthread_barrier_wait(&bar);
+
+ while (atomic_load_explicit(n, memory_order_relaxed) != 0) {
+ /*
+ * Do some things that might take the same lock as
+ * pthread_setcancelstate.
+ */
+ RZ(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL));
+ RZ(pthread_getname_np(pthread_self(), name, sizeof(name)));
+ RZ(pthread_setname_np(pthread_self(), "%s", name));
+ }
+
+ pthread_cleanup_pop(/*execute*/1);
+ return NULL;
+}
+
+ATF_TC(sigsafecancelstate);
+ATF_TC_HEAD(sigsafecancelstate, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test pthread_setcancelstate async-signal-safety");
+}
+ATF_TC_BODY(sigsafecancelstate, tc)
+{
+ pthread_t t;
+ atomic_ulong n = 10000;
+ void *result;
+
+ RZ(pthread_barrier_init(&bar, NULL, 2));
+ RZ(pthread_create(&t, NULL, &sigsafecancelstate, &n));
+
+ (void)pthread_barrier_wait(&bar);
+
+ while (atomic_load_explicit(&n, memory_order_relaxed)) {
+ pthread_kill(t, SIGUSR1);
+ atomic_store_explicit(&n,
+ atomic_load_explicit(&n, memory_order_relaxed) - 1,
+ memory_order_relaxed);
+ }
+
+ alarm(1);
+ RZ(pthread_join(t, &result));
+ ATF_CHECK_MSG(result == NULL, "result=%p", result);
+ ATF_CHECK(cleanup_done);
+}
+
+static void *
+testcancel_loop(void *cookie)
+{
+
+ pthread_cleanup_push(&cleanup, &cleanup_done);
+ (void)pthread_barrier_wait(&bar);
+ for (;;)
+ pthread_testcancel();
+ pthread_cleanup_pop(/*execute*/0);
+
+ return NULL;
+}
+
+ATF_TC(testcancel_loop);
+ATF_TC_HEAD(testcancel_loop, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test pthread_testcancel in a loop");
+}
+ATF_TC_BODY(testcancel_loop, tc)
+{
+ pthread_t t;
+ void *result;
+
+ RZ(pthread_barrier_init(&bar, NULL, 2));
+ RZ(pthread_create(&t, NULL, &testcancel_loop, NULL));
+
+ (void)pthread_barrier_wait(&bar);
+ RZ(pthread_cancel(t));
+
+ alarm(1);
+ RZ(pthread_join(t, &result));
+ ATF_CHECK_MSG(result == PTHREAD_CANCELED,
+ "result=%p PTHREAD_CANCELED=%p", result, PTHREAD_CANCELED);
+ ATF_CHECK(cleanup_done);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ADD_TEST_CANCELPOINT(cancelpoint_accept);
+ ADD_TEST_CANCELPOINT(cancelpoint_accept4);
+ ADD_TEST_CANCELPOINT(cancelpoint_aio_suspend);
+ ADD_TEST_CANCELPOINT(cancelpoint_clock_nanosleep);
+ ADD_TEST_CANCELPOINT(cancelpoint_close);
+ ADD_TEST_CANCELPOINT(cancelpoint_cnd_timedwait);
+ ADD_TEST_CANCELPOINT(cancelpoint_cnd_wait);
+ ADD_TEST_CANCELPOINT(cancelpoint_connect);
+ ADD_TEST_CANCELPOINT(cancelpoint_creat);
+ ADD_TEST_CANCELPOINT(cancelpoint_fcntl_F_SETLKW);
+ ADD_TEST_CANCELPOINT(cancelpoint_fcntl_F_OFD_SETLKW);
+ ADD_TEST_CANCELPOINT(cancelpoint_fdatasync);
+ ADD_TEST_CANCELPOINT(cancelpoint_fsync);
+ ADD_TEST_CANCELPOINT(cancelpoint_kevent);
+ ADD_TEST_CANCELPOINT(cancelpoint_lockf_F_LOCK);
+ ADD_TEST_CANCELPOINT(cancelpoint_mq_receive);
+ ADD_TEST_CANCELPOINT(cancelpoint_mq_send);
+ ADD_TEST_CANCELPOINT(cancelpoint_mq_timedreceive);
+ ADD_TEST_CANCELPOINT(cancelpoint_mq_timedsend);
+ ADD_TEST_CANCELPOINT(cancelpoint_msgrcv);
+ ADD_TEST_CANCELPOINT(cancelpoint_msgsnd);
+ ADD_TEST_CANCELPOINT(cancelpoint_msync);
+ ADD_TEST_CANCELPOINT(cancelpoint_nanosleep);
+ ADD_TEST_CANCELPOINT(cancelpoint_open);
+ ADD_TEST_CANCELPOINT(cancelpoint_openat);
+ ADD_TEST_CANCELPOINT(cancelpoint_pause);
+ ADD_TEST_CANCELPOINT(cancelpoint_poll);
+ ADD_TEST_CANCELPOINT(cancelpoint_posix_close);
+ ADD_TEST_CANCELPOINT(cancelpoint_ppoll);
+ ADD_TEST_CANCELPOINT(cancelpoint_pread);
+ ADD_TEST_CANCELPOINT(cancelpoint_pselect);
+ ADD_TEST_CANCELPOINT(cancelpoint_pthread_cond_clockwait);
+ ADD_TEST_CANCELPOINT(cancelpoint_pthread_cond_timedwait);
+ ADD_TEST_CANCELPOINT(cancelpoint_pthread_cond_wait);
+ ADD_TEST_CANCELPOINT(cancelpoint_pthread_join);
+ ADD_TEST_CANCELPOINT(cancelpoint_pthread_testcancel);
+ ADD_TEST_CANCELPOINT(cancelpoint_pwrite);
+ ADD_TEST_CANCELPOINT(cancelpoint_read);
+ ADD_TEST_CANCELPOINT(cancelpoint_readv);
+ ADD_TEST_CANCELPOINT(cancelpoint_recv);
+ ADD_TEST_CANCELPOINT(cancelpoint_recvfrom);
+ ADD_TEST_CANCELPOINT(cancelpoint_recvmsg);
+ ADD_TEST_CANCELPOINT(cancelpoint_select);
+ ADD_TEST_CANCELPOINT(cancelpoint_send);
+ ADD_TEST_CANCELPOINT(cancelpoint_sendto);
+ ADD_TEST_CANCELPOINT(cancelpoint_sendmsg);
+ ADD_TEST_CANCELPOINT(cancelpoint_sigsuspend);
+ ADD_TEST_CANCELPOINT(cancelpoint_sigtimedwait);
+ ADD_TEST_CANCELPOINT(cancelpoint_sigwait);
+ ADD_TEST_CANCELPOINT(cancelpoint_sigwaitinfo);
+ ADD_TEST_CANCELPOINT(cancelpoint_sleep);
+ ADD_TEST_CANCELPOINT(cancelpoint_tcdrain);
+ ADD_TEST_CANCELPOINT(cancelpoint_thrd_join);
+ ADD_TEST_CANCELPOINT(cancelpoint_thrd_sleep);
+ ADD_TEST_CANCELPOINT(cancelpoint_wait);
+ ADD_TEST_CANCELPOINT(cancelpoint_waitid);
+ ADD_TEST_CANCELPOINT(cancelpoint_waitpid);
+ ADD_TEST_CANCELPOINT(cancelpoint_write);
+ ADD_TEST_CANCELPOINT(cancelpoint_writev);
+
+ ATF_TP_ADD_TC(tp, cleanuppop0);
+ ATF_TP_ADD_TC(tp, cleanuppop1);
+ ATF_TP_ADD_TC(tp, cancelself_async);
+ ATF_TP_ADD_TC(tp, cancelself_deferred);
+ ATF_TP_ADD_TC(tp, defaults);
+ ATF_TP_ADD_TC(tp, disable_enable);
+ ATF_TP_ADD_TC(tp, disable_enable_async);
+ ATF_TP_ADD_TC(tp, disable_enable_setcanceltype_async);
+ ATF_TP_ADD_TC(tp, setcanceltype_async);
+ ATF_TP_ADD_TC(tp, notestcancel_loop_async);
+ ATF_TP_ADD_TC(tp, sigsafecancelstate);
+ ATF_TP_ADD_TC(tp, testcancel_loop);
+
+ return atf_no_error();
+}
diff --git a/lib/libpthread/t_compat_cancel.c b/lib/libpthread/t_compat_cancel.c
new file mode 100644
index 000000000000..280d072b3dd6
--- /dev/null
+++ b/lib/libpthread/t_compat_cancel.c
@@ -0,0 +1,287 @@
+/* $NetBSD: t_compat_cancel.c,v 1.3 2025/04/25 13:09:44 riastradh Exp $ */
+
+/*
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define __LIBC12_SOURCE__ /* expose compat declarations */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_compat_cancel.c,v 1.3 2025/04/25 13:09:44 riastradh Exp $");
+
+#include <sys/event.h>
+#include <sys/mman.h>
+
+#include <aio.h>
+#include <atf-c.h>
+#include <mqueue.h>
+#include <pthread.h>
+#include <signal.h>
+
+#include <compat/sys/event.h>
+#include <compat/sys/mman.h>
+#include <compat/sys/poll.h>
+#include <compat/sys/select.h>
+
+#include <compat/include/aio.h>
+#include <compat/include/mqueue.h>
+#include <compat/include/signal.h>
+#include <compat/include/time.h>
+
+#include "cancelpoint.h"
+#include "h_macros.h"
+
+pthread_barrier_t bar;
+bool cleanup_done;
+
+static void
+cancelpoint_compat100_kevent(void)
+{
+ int kq;
+ struct kevent100 ev;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.ident = SIGUSR1;
+ ev.filter = EVFILT_SIGNAL;
+ ev.flags = EV_ADD|EV_ENABLE;
+ ev.fflags = 0;
+ ev.data = 0;
+ ev.udata = 0;
+
+ RL(kq = kqueue());
+ RL(__kevent50(kq, &ev, 1, NULL, 1, &(const struct timespec){0,0}));
+ cancelpointready();
+ RL(__kevent50(kq, NULL, 0, &ev, 1, NULL));
+}
+
+static void
+cancelpoint_compat12_msync(void)
+{
+ const unsigned long pagesize = sysconf(_SC_PAGESIZE);
+ int fd;
+ void *map;
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0666));
+ RL(ftruncate(fd, pagesize));
+ REQUIRE_LIBC(map = mmap(NULL, pagesize, PROT_READ|PROT_WRITE,
+ MAP_SHARED, fd, 0),
+ MAP_FAILED);
+ cancelpointready();
+ RL(msync(map, pagesize));
+}
+
+static void
+cancelpoint_compat50___sigtimedwait(void)
+{
+ sigset_t mask, omask;
+ siginfo_t info;
+ struct timespec50 t = {.tv_sec = 2, .tv_nsec = 0};
+
+ RL(__sigfillset14(&mask));
+ RL(__sigprocmask14(SIG_BLOCK, &mask, &omask));
+ cancelpointready();
+ RL(__sigtimedwait(&omask, &info, &t));
+}
+
+static void
+cancelpoint_compat50_aio_suspend(void)
+{
+ int fd[2];
+ char buf[32];
+ struct aiocb aio = {
+ .aio_offset = 0,
+ .aio_buf = buf,
+ .aio_nbytes = sizeof(buf),
+ .aio_fildes = -1,
+ };
+ const struct aiocb *const aiolist[] = { &aio };
+
+ RL(pipe(fd));
+ aio.aio_fildes = fd[0];
+ RL(aio_read(&aio));
+ cancelpointready();
+ RL(aio_suspend(aiolist, __arraycount(aiolist), NULL));
+}
+
+static void
+cancelpoint_compat50_kevent(void)
+{
+ int kq;
+ struct kevent100 ev;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.ident = SIGUSR1;
+ ev.filter = EVFILT_SIGNAL;
+ ev.flags = EV_ADD|EV_ENABLE;
+ ev.fflags = 0;
+ ev.data = 0;
+ ev.udata = 0;
+
+ RL(kq = kqueue());
+ RL(kevent(kq, &ev, 1, NULL, 1, &(const struct timespec50){0,0}));
+ cancelpointready();
+ RL(kevent(kq, NULL, 0, &ev, 1, NULL));
+}
+
+static void
+cancelpoint_compat50_mq_timedreceive(void)
+{
+ mqd_t mq;
+ char buf[32];
+ struct timespec50 t = {.tv_sec = 2, .tv_nsec = 0};
+
+ RL(mq = mq_open("mq", O_RDWR|O_CREAT, 0666, NULL));
+ cancelpointready();
+ RL(mq_timedreceive(mq, buf, sizeof(buf), NULL, &t));
+}
+
+static void
+cancelpoint_compat50_mq_timedsend(void)
+{
+ mqd_t mq;
+ char buf[32] = {0};
+ struct timespec50 t = {.tv_sec = 2, .tv_nsec = 0};
+
+ RL(mq = mq_open("mq", O_RDWR|O_CREAT, 0666, NULL));
+ cancelpointready();
+ RL(mq_timedsend(mq, buf, sizeof(buf), 0, &t));
+}
+
+static void
+cancelpoint_compat50_nanosleep(void)
+{
+ struct timespec50 t = {.tv_sec = 2, .tv_nsec = 0};
+
+ cancelpointready();
+ RL(nanosleep(&t, NULL));
+}
+
+static void
+cancelpoint_compat50_pollts(void)
+{
+ int fd[2];
+ struct pollfd pfd;
+ struct timespec50 t = {.tv_sec = 2, .tv_nsec = 0};
+
+ RL(pipe(fd));
+ pfd.fd = fd[0];
+ pfd.events = POLLIN;
+ cancelpointready();
+ RL(pollts(&pfd, 1, &t, NULL));
+}
+
+static void
+cancelpoint_compat50_pselect(void)
+{
+ int fd[2];
+ fd_set readfd;
+ struct timespec50 t = {.tv_sec = 2, .tv_nsec = 0};
+
+ FD_ZERO(&readfd);
+
+ RL(pipe(fd));
+ FD_SET(fd[0], &readfd);
+ cancelpointready();
+ RL(pselect(fd[0] + 1, &readfd, NULL, NULL, &t, NULL));
+}
+
+static void
+cancelpoint_compat50_select(void)
+{
+ int fd[2];
+ fd_set readfd;
+ struct timeval50 t = {.tv_sec = 1, .tv_usec = 0};
+
+ FD_ZERO(&readfd);
+
+ RL(pipe(fd));
+ FD_SET(fd[0], &readfd);
+ cancelpointready();
+ RL(select(fd[0] + 1, &readfd, NULL, NULL, &t));
+}
+
+static void
+cancelpoint_compat13_sigsuspend(void)
+{
+ sigset13_t mask, omask;
+
+ RL(sigfillset(&mask));
+ RL(sigprocmask(SIG_BLOCK, &mask, &omask));
+ cancelpointready();
+ RL(sigsuspend(&omask));
+}
+
+static void
+cancelpoint_compat50_sigtimedwait(void)
+{
+ sigset_t mask, omask;
+ siginfo_t info;
+ struct timespec50 t = {.tv_sec = 2, .tv_nsec = 0};
+
+ RL(__sigfillset14(&mask));
+ RL(__sigprocmask14(SIG_BLOCK, &mask, &omask));
+ cancelpointready();
+ RL(sigtimedwait(&omask, &info, &t));
+}
+
+TEST_CANCELPOINT(cancelpoint_compat100_kevent, __nothing)
+TEST_CANCELPOINT(cancelpoint_compat12_msync, __nothing)
+TEST_CANCELPOINT(cancelpoint_compat13_sigsuspend,
+ atf_tc_expect_signal(-1, "PR lib/59240: POSIX.1-2024:"
+ " cancellation point audit"))
+TEST_CANCELPOINT(cancelpoint_compat50___sigtimedwait,
+ atf_tc_expect_signal(-1, "PR lib/59240: POSIX.1-2024:"
+ " cancellation point audit"))
+TEST_CANCELPOINT(cancelpoint_compat50_aio_suspend, __nothing)
+TEST_CANCELPOINT(cancelpoint_compat50_kevent, __nothing)
+TEST_CANCELPOINT(cancelpoint_compat50_mq_timedreceive, __nothing)
+TEST_CANCELPOINT(cancelpoint_compat50_mq_timedsend, __nothing)
+TEST_CANCELPOINT(cancelpoint_compat50_nanosleep, __nothing)
+TEST_CANCELPOINT(cancelpoint_compat50_pollts, __nothing)
+TEST_CANCELPOINT(cancelpoint_compat50_pselect, __nothing)
+TEST_CANCELPOINT(cancelpoint_compat50_select, __nothing)
+TEST_CANCELPOINT(cancelpoint_compat50_sigtimedwait,
+ atf_tc_expect_signal(-1, "PR lib/59240: POSIX.1-2024:"
+ " cancellation point audit"))
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ADD_TEST_CANCELPOINT(cancelpoint_compat100_kevent);
+ ADD_TEST_CANCELPOINT(cancelpoint_compat12_msync);
+ ADD_TEST_CANCELPOINT(cancelpoint_compat13_sigsuspend);
+ ADD_TEST_CANCELPOINT(cancelpoint_compat50___sigtimedwait);
+ ADD_TEST_CANCELPOINT(cancelpoint_compat50_aio_suspend);
+ ADD_TEST_CANCELPOINT(cancelpoint_compat50_kevent);
+ ADD_TEST_CANCELPOINT(cancelpoint_compat50_mq_timedreceive);
+ ADD_TEST_CANCELPOINT(cancelpoint_compat50_mq_timedsend);
+ ADD_TEST_CANCELPOINT(cancelpoint_compat50_nanosleep);
+ ADD_TEST_CANCELPOINT(cancelpoint_compat50_pollts);
+ ADD_TEST_CANCELPOINT(cancelpoint_compat50_pselect);
+ ADD_TEST_CANCELPOINT(cancelpoint_compat50_select);
+ ADD_TEST_CANCELPOINT(cancelpoint_compat50_sigtimedwait);
+
+ return atf_no_error();
+}
diff --git a/lib/libpthread/t_stack.c b/lib/libpthread/t_stack.c
new file mode 100644
index 000000000000..1c5050d5fd4c
--- /dev/null
+++ b/lib/libpthread/t_stack.c
@@ -0,0 +1,491 @@
+/* $NetBSD: t_stack.c,v 1.6 2023/11/28 02:54:33 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2023 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _KMEMUSER /* __MACHINE_STACK_GROWS_UP */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_stack.c,v 1.6 2023/11/28 02:54:33 riastradh Exp $");
+
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include <uvm/uvm_param.h> /* VM_THREAD_GUARD_SIZE */
+
+#include <atf-c.h>
+#include <pthread.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "h_macros.h"
+
+struct jmp_ctx {
+ jmp_buf buf;
+};
+
+/*
+ * State used by various tests.
+ */
+struct ctx {
+ size_t size; /* default stack size */
+ size_t guardsize; /* default guard size */
+ void *addr; /* user-allocated stack */
+ pthread_key_t jmp_key; /* jmp_ctx to return from SIGSEGV handler */
+} ctx, *C = &ctx;
+
+/*
+ * getdefaultstacksize()
+ *
+ * Return the default stack size for threads created with
+ * pthread_create.
+ */
+static size_t
+getdefaultstacksize(void)
+{
+ pthread_attr_t attr;
+ size_t stacksize;
+
+ /*
+ * When called from the main thread, this returns the default
+ * stack size (pthread__stacksize) used for pthreads.
+ */
+ RZ(pthread_getattr_np(pthread_self(), &attr));
+ RZ(pthread_attr_getstacksize(&attr, &stacksize));
+ RZ(pthread_attr_destroy(&attr));
+
+ /*
+ * Verify that the assumption above holds.
+ */
+ extern size_t pthread__stacksize; /* pthread_int.h */
+ ATF_CHECK_EQ_MSG(stacksize, pthread__stacksize,
+ "stacksize=%zu pthread__stacksize=%zu",
+ stacksize, pthread__stacksize);
+
+ return stacksize;
+}
+
+/*
+ * getnondefaultstacksize()
+ *
+ * Return a stack size that is not the default stack size for
+ * threads created with pthread_create.
+ */
+static size_t
+getnondefaultstacksize(void)
+{
+
+ return getdefaultstacksize() + sysconf(_SC_PAGESIZE);
+}
+
+/*
+ * getdefaultguardsize()
+ *
+ * Return the default guard size for threads created with
+ * pthread_create.
+ */
+static size_t
+getdefaultguardsize(void)
+{
+ const int mib[2] = { CTL_VM, VM_THREAD_GUARD_SIZE };
+ unsigned guardsize;
+ size_t len = sizeof(guardsize);
+
+ RL(sysctl(mib, __arraycount(mib), &guardsize, &len, NULL, 0));
+ ATF_REQUIRE_EQ_MSG(len, sizeof(guardsize),
+ "len=%zu sizeof(guardsize)=%zu", len, sizeof(guardsize));
+
+ /*
+ * Verify this matches what libpthread determined.
+ */
+ extern size_t pthread__guardsize; /* pthread_int.h */
+ ATF_CHECK_EQ_MSG(guardsize, pthread__guardsize,
+ "guardsize=%u pthread__guardsize=%zu",
+ guardsize, pthread__guardsize);
+
+ return guardsize;
+}
+
+/*
+ * alloc(nbytes)
+ *
+ * Allocate an nbytes-long page-aligned read/write region and
+ * return a pointer to it. Abort the test if allocation fails, so
+ * if this function returns it succeeds.
+ */
+static void *
+alloc(size_t nbytes)
+{
+ void *ptr;
+
+ REQUIRE_LIBC((ptr = mmap(/*hint*/NULL, nbytes,
+ PROT_READ|PROT_WRITE, MAP_ANON, /*fd*/-1, /*offset*/0)),
+ MAP_FAILED);
+
+ return ptr;
+}
+
+/*
+ * init(stacksize)
+ *
+ * Initialize state used by various tests with the specified
+ * stacksize.
+ *
+ * Make sure to allocate enough space that even if there shouldn't
+ * be a stack guard (i.e., it should be empty), adjusting the
+ * requested bounds by the default stack guard size will leave us
+ * inside allocated memory.
+ */
+static void
+init(size_t stacksize)
+{
+
+ C->size = stacksize;
+ C->guardsize = getdefaultguardsize();
+ C->addr = alloc(C->size + C->guardsize);
+ RZ(pthread_key_create(&C->jmp_key, NULL));
+}
+
+/*
+ * stack_pointer()
+ *
+ * Return the stack pointer. This is used to verify whether the
+ * stack pointer lie within a certain address range.
+ */
+static __noinline void *
+stack_pointer(void)
+{
+ return __builtin_frame_address(0);
+}
+
+/*
+ * sigsegv_ok(signo)
+ *
+ * Signal handler for SIGSEGV to return to the jmp ctx, to verify
+ * that SIGSEGV happened without crashing.
+ */
+static void
+sigsegv_ok(int signo)
+{
+ struct jmp_ctx *j = pthread_getspecific(C->jmp_key);
+
+ longjmp(j->buf, 1);
+}
+
+/*
+ * checksigsegv(p)
+ *
+ * Verify that reading *p triggers SIGSEGV. Fails test nonfatally
+ * if SIGSEGV doesn't happen.
+ */
+static void
+checksigsegv(const char *p)
+{
+ struct jmp_ctx j;
+ struct sigaction act, oact;
+ volatile struct sigaction oactsave;
+ volatile char v;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = &sigsegv_ok;
+
+ if (setjmp(j.buf) == 0) {
+ pthread_setspecific(C->jmp_key, &j);
+ RL(sigaction(SIGSEGV, &act, &oact));
+ oactsave = oact;
+ v = *p; /* trigger SIGSEGV */
+ atf_tc_fail_nonfatal("failed to trigger SIGSEGV at %p", p);
+ } else {
+ /* return from SIGSEGV handler */
+ oact = oactsave;
+ }
+ RL(sigaction(SIGSEGV, &oact, NULL));
+ pthread_setspecific(C->jmp_key, NULL);
+
+ (void)v; /* suppress unused variable warnings */
+}
+
+/*
+ * checknosigsegv(p)
+ *
+ * Verify that reading *p does not trigger SIGSEGV. Fails test
+ * nonfatally if SIGSEGV happens.
+ */
+static void
+checknosigsegv(const char *p)
+{
+ struct jmp_ctx j;
+ struct sigaction act, oact;
+ volatile struct sigaction oactsave;
+ volatile char v;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = &sigsegv_ok;
+
+ if (setjmp(j.buf) == 0) {
+ pthread_setspecific(C->jmp_key, &j);
+ RL(sigaction(SIGSEGV, &act, &oact));
+ oactsave = oact;
+ v = *p; /* better not trigger SIGSEGV */
+ } else {
+ /* return from SIGSEGV handler */
+ atf_tc_fail_nonfatal("spuriously triggered SIGSEGV at %p", p);
+ oact = oactsave;
+ }
+ RL(sigaction(SIGSEGV, &oact, NULL));
+ pthread_setspecific(C->jmp_key, NULL);
+
+ (void)v; /* suppress unused variable warnings */
+}
+
+/*
+ * checkguardaccessthread(cookie)
+ *
+ * Thread start routine that verifies it has access to the start
+ * and end of its stack, according to pthread_attr_getstack, and
+ * _does not_ have access to the start or end of its stack guard,
+ * above the stack (in stack growth direction) by
+ * pthread_attr_getguardsize bytes.
+ */
+static void *
+checkguardaccessthread(void *cookie)
+{
+ pthread_t t = pthread_self();
+ pthread_attr_t attr;
+ void *addr, *guard;
+ size_t size, guardsize;
+
+ /*
+ * Get the the stack and stack guard parameters.
+ */
+ RZ(pthread_getattr_np(t, &attr));
+ RZ(pthread_attr_getstack(&attr, &addr, &size));
+ RZ(pthread_attr_getguardsize(&attr, &guardsize));
+
+ /*
+ * Determine where the guard starts in virtual address space
+ * (not in stack growth direction).
+ */
+#ifdef __MACHINE_STACK_GROWS_UP
+ guard = (char *)addr + size;
+#else
+ guard = (char *)addr - guardsize;
+#endif
+
+ /*
+ * Verify access to the start and end of the stack itself.
+ */
+ checknosigsegv(addr);
+ checknosigsegv((char *)addr + size - 1);
+
+ /*
+ * Verify no access to the start or end of the stack guard.
+ */
+ checksigsegv(guard);
+ checksigsegv((char *)guard + guardsize - 1);
+
+ return NULL;
+}
+
+/*
+ * checkaddraccessthread(cookie)
+ *
+ * Thread start routine that verifies its stack is [C->addr,
+ * C->addr + C->size), according to pthread_attr_getstack and
+ * pthread_addr_getstacksize, and verifies it has access to that
+ * whole range.
+ */
+static void *
+checkaddraccessthread(void *cookie)
+{
+ pthread_t t = pthread_self();
+ pthread_attr_t attr;
+ void *sp;
+ void *addr;
+ size_t size, size0;
+
+ /*
+ * Verify the stack pointer lies somewhere in the allocated
+ * range.
+ */
+ sp = stack_pointer();
+ ATF_CHECK_MSG(C->addr <= sp, "sp=%p not in [%p,%p + 0x%zu) = [%p,%p)",
+ sp, C->addr, C->addr, C->size, C->addr, (char *)C->addr + C->size);
+ ATF_CHECK_MSG(sp <= (void *)((char *)C->addr + C->size),
+ "sp=%p not in [%p,%p + 0x%zu) = [%p,%p)",
+ sp, C->addr, C->addr, C->size, C->addr, (char *)C->addr + C->size);
+
+ /*
+ * Verify, if not that, then the stack pointer at least lies
+ * within the extra buffer we allocated for slop to address a
+ * bug NetBSD libpthread used to have of spuriously adding the
+ * guard size to a user-allocated stack address. This is
+ * ATF_REQUIRE, not ATF_CHECK, because if this doesn't hold, we
+ * might be clobbering some other memory like malloc pages,
+ * causing the whole test to crash with useless diagnostics.
+ */
+ ATF_REQUIRE_MSG(sp <= (void *)((char *)C->addr + C->size +
+ C->guardsize),
+ "sp=%p not even in buffer [%p,%p + 0x%zu + 0x%zu) = [%p,%p)",
+ sp, C->addr, C->addr, C->size, C->guardsize,
+ C->addr, (char *)C->addr + C->size + C->guardsize);
+
+ /*
+ * Get the stack parameters -- both via pthread_attr_getstack
+ * and via pthread_attr_getstacksize, to make sure they agree
+ * -- and verify that they are what we expect from the caller.
+ */
+ RZ(pthread_getattr_np(t, &attr));
+ RZ(pthread_attr_getstack(&attr, &addr, &size));
+ RZ(pthread_attr_getstacksize(&attr, &size0));
+ ATF_CHECK_EQ_MSG(C->addr, addr, "expected %p actual %p",
+ C->addr, addr);
+ ATF_CHECK_EQ_MSG(C->size, size, "expected %zu actual %zu",
+ C->size, size);
+ ATF_CHECK_EQ_MSG(C->size, size0, "expected %zu actual %zu",
+ C->size, size0);
+
+ /*
+ * Verify that we have access to what we expect the stack to
+ * be.
+ */
+ checknosigsegv(C->addr);
+ checknosigsegv((char *)C->addr + C->size - 1);
+
+ return NULL;
+}
+
+ATF_TC(stack1);
+ATF_TC_HEAD(stack1, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test allocating and reallocating a thread with a user stack");
+}
+ATF_TC_BODY(stack1, tc)
+{
+ pthread_attr_t attr;
+ pthread_t t, t2;
+
+ /*
+ * Allocate a stack with a non-default size to verify
+ * libpthread didn't choose the stack size for us.
+ */
+ init(getnondefaultstacksize());
+
+ /*
+ * Create a thread with user-allocated stack of a non-default
+ * size to verify the stack size and access.
+ */
+ RZ(pthread_attr_init(&attr));
+ RZ(pthread_attr_setstack(&attr, C->addr, C->size));
+ RZ(pthread_create(&t, &attr, &checkaddraccessthread, C));
+ RZ(pthread_join(t, NULL));
+
+ /*
+ * Create another thread with the same parameters, and verify
+ * that (a) it was recycled, and (b) it works the same way.
+ */
+ RZ(pthread_create(&t2, &attr, &checkaddraccessthread, C));
+ ATF_CHECK_EQ_MSG(t, t2, "t=%p t2=%p", t, t2); /* NetBSD recycles */
+ RZ(pthread_join(t2, NULL));
+}
+
+ATF_TC(stack2);
+ATF_TC_HEAD(stack2, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test reallocating a thread with a newly self-allocated stack");
+}
+ATF_TC_BODY(stack2, tc)
+{
+ pthread_attr_t attr, attr2;
+ size_t size, size2;
+ pthread_t t, t2;
+
+ /*
+ * Allocate a stack with the default size so that we verify
+ * when libpthread reuses the thread, it doesn't inadvertently
+ * reuse the libpthread-allocated stack too and instead
+ * correctly uses our user-allocated stack.
+ */
+ init(getdefaultstacksize());
+
+ /*
+ * Create a thread with a libpthread-allocated stack that
+ * verifies
+ * (a) access to its own stack, and
+ * (b) no access to its own guard pages;
+ * then get its attributes and wait for it to complete.
+ */
+ RZ(pthread_create(&t, NULL, &checkguardaccessthread, C));
+ RZ(pthread_getattr_np(t, &attr));
+ RZ(pthread_join(t, NULL));
+
+ /*
+ * Create a thread with a user-allocated stack that verifies
+ * (a) stack addr/size match request, and
+ * (b) access to the requested stack,
+ * and confirm that the first thread was recycled -- not part
+ * of POSIX semantics, but part of NetBSD's implementation;
+ * this way, we verify that, even though the thread is
+ * recycled, the thread's stack is set to the user-allocated
+ * stack and access to it works as expected. Then wait for it
+ * to complete.
+ */
+ RZ(pthread_attr_init(&attr2));
+ RZ(pthread_attr_setstack(&attr2, C->addr, C->size));
+ RZ(pthread_create(&t2, &attr2, &checkaddraccessthread, C));
+ ATF_CHECK_EQ_MSG(t, t2, "t=%p t2=%p", t, t2); /* NetBSD recycles */
+ RZ(pthread_join(t2, NULL));
+
+ /*
+ * Verify that the libpthread-allocated stack and
+ * user-allocated stack had the same size, since we chose the
+ * default size.
+ *
+ * Note: We can't say anything about the guard size, because
+ * with pthread_attr_setstack, the guard size is ignored, and
+ * it's not clear from POSIX whether any meaningful guard size
+ * is stored for retrieval with pthread_attr_getguardsize in
+ * attributes with pthread_attr_setstack.
+ */
+ RZ(pthread_attr_getstacksize(&attr, &size));
+ RZ(pthread_attr_getstacksize(&attr2, &size2));
+ ATF_CHECK_EQ_MSG(size, size2, "size=%zu size2=%zu", size, size2);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, stack1);
+ ATF_TP_ADD_TC(tp, stack2);
+
+ return atf_no_error();
+}
diff --git a/lib/libpthread/weak/Makefile b/lib/libpthread/weak/Makefile
new file mode 100644
index 000000000000..f7cdd75c2723
--- /dev/null
+++ b/lib/libpthread/weak/Makefile
@@ -0,0 +1,25 @@
+# $NetBSD: Makefile,v 1.2 2025/10/18 20:27:23 riastradh Exp $
+#
+
+TESTSDIR= ${TESTSBASE}/lib/libpthread/weak
+
+TESTS_C+= t_pthread_weak_nothread
+TESTS_C+= t_pthread_weak_threaded
+
+CPPFLAGS+= -I${.CURDIR}/lib
+
+.include <bsd.own.mk> # PRINTOBJDIR
+
+.if !defined(H_PTHREAD_WEAK_OBJDIR)
+H_PTHREAD_WEAK_OBJDIR!= cd ${.CURDIR}/lib && ${PRINTOBJDIR}
+.MAKEOVERRIDES+= H_PTHREAD_WEAK_OBJDIR
+.endif
+
+LDADD+= -L${H_PTHREAD_WEAK_OBJDIR}
+LDADD+= -Wl,-rpath,${TESTSBASE}/lib/libpthread/weak
+LDADD+= -lh_pthread_weak
+LDADD.t_pthread_weak_threaded+= -lpthread
+
+SUBDIR+= lib
+
+.include <bsd.test.mk>
diff --git a/lib/libpthread/weak/Makefile.inc b/lib/libpthread/weak/Makefile.inc
new file mode 100644
index 000000000000..921a499b55ba
--- /dev/null
+++ b/lib/libpthread/weak/Makefile.inc
@@ -0,0 +1 @@
+.include "${.PARSEDIR}/../../Makefile.inc"
diff --git a/lib/libpthread/weak/lib/Makefile b/lib/libpthread/weak/lib/Makefile
new file mode 100644
index 000000000000..0976a72efd27
--- /dev/null
+++ b/lib/libpthread/weak/lib/Makefile
@@ -0,0 +1,21 @@
+# $NetBSD: Makefile,v 1.2 2026/01/21 17:57:27 christos Exp $
+#
+
+MKPROFILE= no # XXX hack -- should be NOPROFILE
+NOLINT= # defined
+NOPICINSTALL= # defined
+NOMAN= # defined
+NOSTATICLIB= # defined
+
+LIB= h_pthread_weak
+SRCS+= h_pthread_weak.c
+
+LDADD+= -latf-c
+
+LIBDIR= ${TESTSBASE}/lib/libpthread/weak
+SHLIBDIR= ${TESTSBASE}/lib/libpthread/weak
+SHLIB_MAJOR= 1
+
+LIBISCXX= yes
+
+.include <bsd.lib.mk>
diff --git a/lib/libpthread/weak/lib/h_pthread_weak.c b/lib/libpthread/weak/lib/h_pthread_weak.c
new file mode 100644
index 000000000000..d8b9e624c07d
--- /dev/null
+++ b/lib/libpthread/weak/lib/h_pthread_weak.c
@@ -0,0 +1,83 @@
+/* $NetBSD: h_pthread_weak.c,v 1.1 2025/10/06 13:16:44 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: h_pthread_weak.c,v 1.1 2025/10/06 13:16:44 riastradh Exp $");
+
+#define _NETBSD_PTHREAD_CREATE_WEAK
+
+#include "h_pthread_weak.h"
+
+#include <atf-c.h>
+#include <pthread.h>
+
+#include "h_macros.h"
+
+static void *
+start(void *cookie)
+{
+ return cookie;
+}
+
+void
+test_mutex(void)
+{
+ pthread_mutex_t mtx;
+
+ RZ(pthread_mutex_init(&mtx, NULL));
+ RZ(pthread_mutex_lock(&mtx));
+ RZ(pthread_mutex_unlock(&mtx));
+ RZ(pthread_mutex_destroy(&mtx));
+}
+
+void
+test_thread_creation(void)
+{
+ int cookie = 123;
+ pthread_attr_t attr;
+ pthread_t t;
+ void *result;
+
+ RZ(pthread_attr_init(&attr));
+ RZ(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE));
+ RZ(pthread_create(&t, NULL, &start, &cookie));
+ RZ(pthread_attr_destroy(&attr));
+ RZ(pthread_join(t, &result));
+ ATF_CHECK_EQ(result, &cookie);
+}
+
+void
+test_thread_creation_failure(void)
+{
+ int cookie = 123;
+ pthread_t t;
+ int error;
+
+ error = pthread_create(&t, NULL, &start, &cookie);
+ ATF_CHECK_MSG(error != 0, "pthread_create unexpectedly succeeded");
+}
diff --git a/lib/libpthread/weak/lib/h_pthread_weak.h b/lib/libpthread/weak/lib/h_pthread_weak.h
new file mode 100644
index 000000000000..b970d2b020c6
--- /dev/null
+++ b/lib/libpthread/weak/lib/h_pthread_weak.h
@@ -0,0 +1,36 @@
+/* $NetBSD: h_pthread_weak.h,v 1.1 2025/10/06 13:16:44 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef H_PTHREAD_WEAK_H
+#define H_PTHREAD_WEAK_H
+
+void test_mutex(void);
+void test_thread_creation(void);
+void test_thread_creation_failure(void);
+
+#endif /* H_PTHREAD_WEAK_H */
diff --git a/lib/libpthread/weak/t_pthread_weak_nothread.c b/lib/libpthread/weak/t_pthread_weak_nothread.c
new file mode 100644
index 000000000000..a5447b695dbb
--- /dev/null
+++ b/lib/libpthread/weak/t_pthread_weak_nothread.c
@@ -0,0 +1,64 @@
+/* $NetBSD: t_pthread_weak_nothread.c,v 1.1 2025/10/18 20:27:23 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_pthread_weak_nothread.c,v 1.1 2025/10/18 20:27:23 riastradh Exp $");
+
+#include <atf-c.h>
+
+#include "h_pthread_weak.h"
+
+ATF_TC(mutex);
+ATF_TC_HEAD(mutex, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test mutex usage in library with _NETBSD_PTHREAD_CREATE_WEAK");
+}
+ATF_TC_BODY(mutex, tc)
+{
+ test_mutex();
+}
+
+ATF_TC(thread_creation_failure);
+ATF_TC_HEAD(thread_creation_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test pthread_create via library fails in no-thread application");
+}
+ATF_TC_BODY(thread_creation_failure, tc)
+{
+ test_thread_creation_failure();
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, mutex);
+ ATF_TP_ADD_TC(tp, thread_creation_failure);
+
+ return atf_no_error();
+}
diff --git a/lib/libpthread/weak/t_pthread_weak_threaded.c b/lib/libpthread/weak/t_pthread_weak_threaded.c
new file mode 100644
index 000000000000..70c649ea13b6
--- /dev/null
+++ b/lib/libpthread/weak/t_pthread_weak_threaded.c
@@ -0,0 +1,64 @@
+/* $NetBSD: t_pthread_weak_threaded.c,v 1.1 2025/10/18 20:27:23 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_pthread_weak_threaded.c,v 1.1 2025/10/18 20:27:23 riastradh Exp $");
+
+#include <atf-c.h>
+
+#include "h_pthread_weak.h"
+
+ATF_TC(mutex);
+ATF_TC_HEAD(mutex, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test mutex usage in library with _NETBSD_PTHREAD_CREATE_WEAK");
+}
+ATF_TC_BODY(mutex, tc)
+{
+ test_mutex();
+}
+
+ATF_TC(thread_creation);
+ATF_TC_HEAD(thread_creation, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test pthread_create via library in threaded application");
+}
+ATF_TC_BODY(thread_creation, tc)
+{
+ test_thread_creation();
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, mutex);
+ ATF_TP_ADD_TC(tp, thread_creation);
+
+ return atf_no_error();
+}
diff --git a/lib/libstdc++/Makefile b/lib/libstdc++/Makefile
new file mode 100644
index 000000000000..3562d78dd70c
--- /dev/null
+++ b/lib/libstdc++/Makefile
@@ -0,0 +1,12 @@
+# $NetBSD: Makefile,v 1.1 2024/04/28 01:21:27 riastradh Exp $
+#
+
+NOMAN= # defined
+
+TESTSDIR= ${TESTSBASE}/lib/libstdc++
+BINDIR= ${TESTSDIR}
+
+TESTS_SH+= t_sync_with_stdio
+PROG_CXX+= h_cin_nosync
+
+.include <bsd.test.mk>
diff --git a/lib/libstdc++/h_cin_nosync.cc b/lib/libstdc++/h_cin_nosync.cc
new file mode 100644
index 000000000000..a934e2c67415
--- /dev/null
+++ b/lib/libstdc++/h_cin_nosync.cc
@@ -0,0 +1,40 @@
+/* $NetBSD: h_cin_nosync.cc,v 1.1 2024/04/28 01:21:27 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2024 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <iostream>
+
+int
+main(void)
+{
+ char buf[128];
+
+ std::ios::sync_with_stdio(false);
+ std::cin.read(buf, sizeof(buf));
+ std::cout << std::cin.gcount() << std::endl;
+ return 0;
+}
diff --git a/lib/libstdc++/t_sync_with_stdio.sh b/lib/libstdc++/t_sync_with_stdio.sh
new file mode 100644
index 000000000000..fe76c49b988f
--- /dev/null
+++ b/lib/libstdc++/t_sync_with_stdio.sh
@@ -0,0 +1,41 @@
+# $NetBSD: t_sync_with_stdio.sh,v 1.2 2024/05/20 11:20:53 riastradh Exp $
+#
+# Copyright (c) 2024 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+cin_nosync_head()
+{
+ atf_set descr "Check cin works after std::ios::sync_with_stdio(false)"
+}
+cin_nosync_body()
+{
+ echo hello >in
+ atf_check -o inline:'6\n' "$(atf_get_srcdir)"/h_cin_nosync <in
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case cin_nosync
+}
diff --git a/lib/libutil/t_strpct.c b/lib/libutil/t_strpct.c
new file mode 100644
index 000000000000..c061ae152321
--- /dev/null
+++ b/lib/libutil/t_strpct.c
@@ -0,0 +1,202 @@
+/* $NetBSD: t_strpct.c,v 1.2 2025/05/03 07:22:52 rillig Exp $ */
+
+/*
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code was contributed to The NetBSD Foundation by Roland Illig.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__COPYRIGHT("@(#) Copyright (c) 2025\
+ The NetBSD Foundation, inc. All rights reserved.");
+__RCSID("$NetBSD: t_strpct.c,v 1.2 2025/05/03 07:22:52 rillig Exp $");
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <util.h>
+
+#include <atf-c.h>
+
+static void
+check_strspct(const char *file, unsigned line,
+ size_t bufsiz, intmax_t num, intmax_t den, size_t digits,
+ const char *want)
+{
+ char buf[128];
+
+ ATF_REQUIRE_MSG(bufsiz < sizeof(buf) - 2, "bufsiz too large");
+ memset(buf, '>', sizeof(buf));
+ buf[0] = '<';
+ buf[sizeof(buf) - 1] = '\0';
+
+ const char *have = strspct(buf + 1, bufsiz, num, den, digits);
+
+ ATF_REQUIRE_MSG(buf[0] == '<',
+ "out-of-bounds write before");
+ ATF_REQUIRE_MSG(buf[1 + bufsiz] == '>',
+ "out-of-bounds write after");
+ ATF_REQUIRE_MSG(have == buf + 1,
+ "have != buf");
+ ATF_CHECK_MSG(bufsiz > 0 ? strcmp(have, want) == 0 : true,
+ "%s:%u: want \"%s\", have \"%s\"",
+ file, line, want, have);
+}
+
+#define h_strspct(bufsiz, num, den, digits, want) \
+ check_strspct(__FILE__, __LINE__, bufsiz, num, den, digits, want)
+
+static void
+check_strpct(const char *file, unsigned line,
+ size_t bufsiz, uintmax_t num, uintmax_t den, size_t digits,
+ const char *want)
+{
+ char buf[128];
+
+ ATF_REQUIRE_MSG(bufsiz < sizeof(buf) - 2, "bufsiz too large");
+ memset(buf, '>', sizeof(buf));
+ buf[0] = '<';
+ buf[sizeof(buf) - 1] = '\0';
+
+ const char *have = strpct(buf + 1, bufsiz, num, den, digits);
+
+ ATF_REQUIRE_MSG(buf[0] == '<',
+ "out-of-bounds write before");
+ ATF_REQUIRE_MSG(buf[1 + bufsiz] == '>',
+ "out-of-bounds write after");
+ ATF_REQUIRE_MSG(have == buf + 1,
+ "have != buf");
+ ATF_CHECK_MSG(bufsiz > 0 ? strcmp(have, want) == 0 : true,
+ "%s:%u: want \"%s\", have \"%s\"",
+ file, line, want, have);
+}
+
+#define h_strpct(bufsiz, num, den, digits, want) \
+ check_strpct(__FILE__, __LINE__, bufsiz, num, den, digits, want)
+
+ATF_TC(strspct);
+ATF_TC_HEAD(strspct, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks strspct(3)");
+}
+ATF_TC_BODY(strspct, tc)
+{
+
+ // Very small buffers.
+ h_strspct(0, 0, 0, 0, "");
+ h_strspct(1, 0, 0, 0, "");
+
+ // Small buffers.
+ h_strspct(2, 1, 40, 0, "2");
+ h_strspct(3, 1, 40, 0, "2");
+ h_strspct(3, 1, 40, 1, "2.");
+ h_strspct(4, 1, 40, 1, "2.5");
+ h_strspct(4, 8, 40, 1, "20.");
+ h_strspct(6, 1, 5, 1, "20.0");
+ h_strspct(100, 1, 5, 5, "20.00000");
+ h_strspct( 5, 11223344, 100, 10, "1122");
+ h_strspct(10, 11223344, 100, 10, "11223344.");
+ h_strspct(11, 11223344, 100, 10, "11223344.0");
+
+ // Small buffers with negative numbers.
+ h_strspct(1, -1, 40, 0, "");
+ h_strspct(2, -1, 40, 0, "-");
+ h_strspct(3, -1, 40, 0, "-2");
+ h_strspct(3, -1, 40, 1, "-2");
+ h_strspct(4, -1, 40, 1, "-2.");
+ h_strspct(5, -1, 40, 1, "-2.5");
+ h_strspct(4, -8, 40, 1, "-20");
+ h_strspct(5, -8, 40, 1, "-20.");
+ h_strspct(6, -1, 5, 1, "-20.0");
+ h_strspct(100, -1, 5, 5, "-20.00000");
+ h_strspct( 5, -11223344, 100, 10, "-112");
+ h_strspct(10, -11223344, 100, 10, "-11223344");
+ h_strspct(11, -11223344, 100, 10, "-11223344.");
+ h_strspct(12, -11223344, 100, 10, "-11223344.0");
+
+ // Percentages are always rounded towards zero.
+ h_strspct(6, 1, 6, 1, "16.6");
+ h_strspct(7, -1, 6, 1, "-16.6");
+ h_strspct(7, 1, -6, 1, "-16.6");
+ h_strspct(7, -1, -6, 1, "16.6");
+ h_strspct(100, 1, 7, 20, "14.28571428571428571428");
+
+ // Big numbers.
+ h_strspct(100, INTMAX_MAX, INTMAX_MAX, 0, "100");
+ h_strspct(100, INTMAX_MIN, INTMAX_MIN, 25, "100.0000000000000000000000000");
+ h_strspct(100, INTMAX_MIN, INTMAX_MAX, 25, "-100.0000000000000000108420217");
+ h_strspct(100, INTMAX_MAX, INTMAX_MIN, 25, "-99.9999999999999999891579782");
+ h_strspct(100, INTMAX_MAX, INTMAX_MAX, 25, "100.0000000000000000000000000");
+}
+
+ATF_TC(strpct);
+ATF_TC_HEAD(strpct, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks strpct(3)");
+}
+ATF_TC_BODY(strpct, tc)
+{
+
+ // Small buffers.
+ h_strpct(0, 0, 0, 0, "");
+ h_strpct(1, 0, 0, 0, "");
+ h_strpct(2, 0, 0, 0, "0");
+ h_strpct(3, 0, 0, 0, "0");
+ h_strpct(3, 0, 0, 1, "0.");
+ h_strpct(4, 0, 0, 1, "0.0");
+ h_strpct(4, 1, 5, 1, "20.");
+ h_strpct(6, 1, 5, 1, "20.0");
+ h_strpct(100, 1, 5, 5, "20.00000");
+
+ h_strpct(100, 1, 7, 20, "14.28571428571428571428");
+
+ h_strpct( 5, 11223344, 100, 10, "1122");
+ h_strpct(10, 11223344, 100, 10, "11223344.");
+ h_strpct(11, 11223344, 100, 10, "11223344.0");
+
+ h_strpct(100, UINTMAX_MAX, UINTMAX_MAX, 0, "100");
+ h_strpct(100, UINTMAX_MAX, UINTMAX_MAX, 1, "100.0");
+ h_strpct(100, UINTMAX_MAX, UINTMAX_MAX, 5, "100.00000");
+ h_strpct(100, UINTMAX_MAX, UINTMAX_MAX, 10, "100.0000000000");
+ h_strpct(100, UINTMAX_MAX, UINTMAX_MAX, 15, "100.000000000000000");
+ h_strpct(100, UINTMAX_MAX, UINTMAX_MAX, 20, "100.00000000000000000000");
+ h_strpct(100, UINTMAX_MAX, UINTMAX_MAX, 25, "100.0000000000000000000000000");
+
+ h_strpct(100, UINTMAX_MAX - 1, UINTMAX_MAX, 25, "99.9999999999999999945789891");
+ h_strpct(100, 1, (UINTMAX_MAX >> 1) + 1, 70,
+ "0.0000000000000000108420217248550443400745280086994171142578125000000000");
+ h_strpct(100, UINTMAX_MAX, 1, 10, "1844674407370955161500.0000000000");
+ h_strpct(100, 1, UINTMAX_MAX, 30, "0.000000000000000005421010862427");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, strspct);
+ ATF_TP_ADD_TC(tp, strpct);
+
+ return atf_no_error();
+}
diff --git a/libexec/ld.elf_so/h_r_rel.c b/libexec/ld.elf_so/h_r_rel.c
new file mode 100644
index 000000000000..17cece208ebe
--- /dev/null
+++ b/libexec/ld.elf_so/h_r_rel.c
@@ -0,0 +1,147 @@
+/* $NetBSD: h_r_rel.c,v 1.2 2025/12/21 19:08:09 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: h_r_rel.c,v 1.2 2025/12/21 19:08:09 riastradh Exp $");
+
+#include <stddef.h>
+#include <stdio.h>
+
+/*
+ * When built as position-independent executable, the value of foop and
+ * foopp should be computed either via R_*_RELATIVE or R_*_REL32 or
+ * similar, which -- ports that support it -- may be compressed into a
+ * SHT_RELR section.
+ *
+ * One pointer indirection is enough to produce this effect, but we use
+ * two pointer indirections to increase the probability of a crash in
+ * case the relocations are done wrong.
+ */
+static int foo = 0x5f4d7635;
+static int *volatile foop = &foo;
+static int *volatile *volatile foopp = &foop;
+
+/*
+ * The RELR section compresses relocations for adjacent addresses into
+ * bitmaps of 31 or 63 bits apiece. Create a bunch of consecutive
+ * addresses to relocate, punctuated by the occasional non-relocated
+ * address (null), to check for fencepost errors in the bitmap
+ * iteration.
+ */
+static int bar[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+};
+
+static int *volatile barp[] = {
+ &bar[0x00], &bar[0x01], &bar[0x02], &bar[0x03],
+ &bar[0x04], &bar[0x05], &bar[0x06], &bar[0x07],
+ &bar[0x08], &bar[0x09], &bar[0x0a], &bar[0x0b],
+ &bar[0x0c], &bar[0x0d], &bar[0x0e], &bar[0x0f],
+ &bar[0x10], &bar[0x11], &bar[0x12], &bar[0x13],
+ &bar[0x14], &bar[0x15], &bar[0x16], &bar[0x17],
+ &bar[0x18], &bar[0x19], &bar[0x1a], &bar[0x1b],
+ &bar[0x1c], &bar[0x1d], &bar[0x1e], &bar[0x1f],
+ &bar[0x20], &bar[0x21], &bar[0x22], &bar[0x23],
+ &bar[0x24], &bar[0x25], &bar[0x26], &bar[0x27],
+ &bar[0x28], &bar[0x29], &bar[0x2a], &bar[0x2b],
+ &bar[0x2c], &bar[0x2d], &bar[0x2e], &bar[0x2f],
+ &bar[0x30], &bar[0x31], &bar[0x32], &bar[0x33],
+ &bar[0x34], &bar[0x35], &bar[0x36], &bar[0x37],
+ &bar[0x38], &bar[0x39], &bar[0x3a], &bar[0x3b],
+ &bar[0x3c], &bar[0x3d], &bar[0x3e], &bar[0x3f],
+ NULL, &bar[0x41], &bar[0x42], &bar[0x43], /* test a clear bit */
+ &bar[0x44], &bar[0x45], &bar[0x46], &bar[0x47],
+ &bar[0x48], &bar[0x49], &bar[0x4a], &bar[0x4b],
+ &bar[0x4c], &bar[0x4d], &bar[0x4e], &bar[0x4f],
+ &bar[0x50], &bar[0x51], &bar[0x52], &bar[0x53],
+ &bar[0x54], &bar[0x55], &bar[0x56], &bar[0x57],
+ &bar[0x58], &bar[0x59], &bar[0x5a], &bar[0x5b],
+ &bar[0x5c], &bar[0x5d], &bar[0x5e], &bar[0x5f],
+ &bar[0x60], &bar[0x61], &bar[0x62], &bar[0x63],
+ &bar[0x64], &bar[0x65], &bar[0x66], &bar[0x67],
+ &bar[0x68], &bar[0x69], &bar[0x6a], &bar[0x6b],
+ &bar[0x6c], &bar[0x6d], &bar[0x6e], &bar[0x6f],
+ &bar[0x70], &bar[0x71], &bar[0x72], &bar[0x73],
+ &bar[0x74], &bar[0x75], &bar[0x76], &bar[0x77],
+ &bar[0x78], &bar[0x79], &bar[0x7a], &bar[0x7b],
+ &bar[0x7c], &bar[0x7d], &bar[0x7e], &bar[0x7f],
+ NULL, /* confirm we stop at the end */
+};
+
+static int baz = -0x1adbd477;
+static int *volatile bazp = &baz;
+
+int
+main(void)
+{
+ int i, result = 0;
+
+ if (**foopp != 0x5f4d7635) {
+ fprintf(stderr, "foo @ %p, foop = %p, *foop = %p,"
+ " **foopp = 0x%x\n",
+ &foo, foop, *foopp, **foopp);
+ result |= 1;
+ }
+ for (i = 0; i < (int)__arraycount(barp); i++) {
+ if (i == 0x40 || i == 0x80) {
+ if (barp[i] != NULL) {
+ fprintf(stderr, "barp[%u] = %p\n",
+ i, barp[i]);
+ }
+ } else {
+ if (*barp[i] != i) {
+ fprintf(stderr, "bar[%u] @ %p, barp[%u] = %p,"
+ " *barp[%u] = %u\n",
+ i, &bar[i], i, barp[i], i, *barp[i]);
+ result |= 1;
+ }
+ }
+ }
+ if (*bazp != -0x1adbd477) {
+ fprintf(stderr, "baz @ %p, bazp = %p, *bazp = 0x%x\n",
+ &baz, bazp, *bazp);
+ result |= 1;
+ }
+
+ return result;
+}
diff --git a/libexec/ld.elf_so/t_dladdr.c b/libexec/ld.elf_so/t_dladdr.c
new file mode 100644
index 000000000000..5cdb0148fecf
--- /dev/null
+++ b/libexec/ld.elf_so/t_dladdr.c
@@ -0,0 +1,171 @@
+/* $NetBSD: t_dladdr.c,v 1.2 2025/12/15 02:36:47 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_dladdr.c,v 1.2 2025/12/15 02:36:47 riastradh Exp $");
+
+#include <sys/mman.h>
+
+#include <atf-c.h>
+#include <dlfcn.h>
+#include <unistd.h>
+
+#include "h_macros.h"
+
+#define SELF "t_dladdr"
+
+/*
+ * Note: the symbols foo, bar, and baz must be exposed to dlfcn(3) by
+ * linking this with -export-dynamic.
+ */
+int foo; /* something in this ELF object */
+int bar; /* something in this ELF object */
+int baz; /* something in this ELF object */
+
+extern char _end[]; /* one past last byte of this ELF object */
+
+ATF_TC(dladdr_self);
+ATF_TC_HEAD(dladdr_self, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify dladdr of data in this object returns self");
+}
+ATF_TC_BODY(dladdr_self, tc)
+{
+ Dl_info info;
+ const char *ptr = (char *)&bar + 1;
+ const char *p;
+
+ /*
+ * If we're statically linked, dladdr just fails (XXX is that
+ * right?). But we're not statically linked because these are
+ * the ld.elf_so tests (XXX should migrate this to
+ * tests/lib/libc/dlfcn/ and handle it there).
+ *
+ * If we're dynamically linked, then foo, bar, and baz should
+ * live in self.
+ */
+ ATF_CHECK_MSG(dladdr(ptr, &info) != 0,
+ "[bar @ %p + [0,%zu)] dladdr(%p) failed: %s",
+ &bar, sizeof(bar), ptr, dlerror());
+ p = strrchr(info.dli_fname, '/');
+ if (p == NULL)
+ p = info.dli_fname;
+ else
+ p++;
+ ATF_CHECK_MSG(strcmp(p, SELF) == 0,
+ "[bar @ %p + [0,%zu)] dladdr found %p in %s, not self=%s",
+ &bar, sizeof(bar), ptr, info.dli_fname, SELF);
+ ATF_CHECK_MSG(strcmp(info.dli_sname, "bar") == 0,
+ "[bar @ %p + [0,%zu)] dladdr found %p in %s at %p, not bar",
+ &bar, sizeof(bar), ptr, info.dli_sname, info.dli_saddr);
+}
+
+ATF_TC(dladdr_errno);
+ATF_TC_HEAD(dladdr_errno, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify dladdr(errno) returns libc.so (or self if static)");
+}
+ATF_TC_BODY(dladdr_errno, tc)
+{
+ Dl_info info;
+ const char *p;
+
+ /*
+ * If we're statically linked, dladdr just fails (XXX is that
+ * right?). But we're not statically linked because these are
+ * the ld.elf_so tests (XXX should migrate this to
+ * tests/lib/libc/dlfcn/ and handle it there).
+ *
+ * If we're dynamically linked and single-threaded (no
+ * libpthread.so), &errno will be in libc.
+ *
+ * If we're dynamically linked and multi-threaded, &errno would
+ * be in a pthread_t object -- but we're not multithreaded, so
+ * it's not. Hence only two cases: static vs dynamic.
+ */
+ ATF_CHECK_MSG(dladdr(&errno, &info) != 0,
+ "[errno @ %p] dladdr failed: %s", &errno, dlerror());
+ p = strrchr(info.dli_fname, '/');
+ if (p == NULL)
+ p = info.dli_fname;
+ else
+ p++;
+ ATF_CHECK_MSG(strcmp(p, SELF) != 0,
+ "[errno @ %p] dladdr found errno in self=%s, not in libc.so",
+ &errno, info.dli_fname);
+}
+
+ATF_TC(dladdr_after__end);
+ATF_TC_HEAD(dladdr_after__end, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify dladdr doesn't claim pages past _end for self");
+}
+ATF_TC_BODY(dladdr_after__end, tc)
+{
+ const size_t pagesize = sysconf(_SC_PAGESIZE);
+ uintptr_t endp, nextp;
+ void *page;
+ Dl_info info;
+
+ /*
+ * Round up to a page start.
+ */
+ endp = (uintptr_t)(void *)_end;
+ nextp = pagesize*((endp + pagesize - 1)/pagesize);
+
+ /*
+ * Map the next page, or something after it. It just has to be
+ * not mapped by an existing object, but for the sake of
+ * testing for past bugs, we would like to make it as close to
+ * a real object as we can.
+ */
+ REQUIRE_LIBC(page = mmap((void *)nextp, pagesize, PROT_NONE, MAP_ANON,
+ /*fd*/-1, /*off*/0),
+ MAP_FAILED);
+
+ /*
+ * Verify dladdr doesn't return anything for this page.
+ */
+ ATF_CHECK_MSG(dladdr(page, &info) == 0,
+ "dladdr returned %s @ %p (symbol %s @ %p) for bogus address %p",
+ info.dli_fname, info.dli_fbase,
+ info.dli_sname, info.dli_saddr,
+ page);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, dladdr_after__end);
+ ATF_TP_ADD_TC(tp, dladdr_errno);
+ ATF_TP_ADD_TC(tp, dladdr_self);
+ return atf_no_error();
+}
diff --git a/libexec/ld.elf_so/t_dlclose_thread.c b/libexec/ld.elf_so/t_dlclose_thread.c
new file mode 100644
index 000000000000..3b80990c1fec
--- /dev/null
+++ b/libexec/ld.elf_so/t_dlclose_thread.c
@@ -0,0 +1,98 @@
+/* $NetBSD: t_dlclose_thread.c,v 1.1 2025/11/23 22:01:13 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_dlclose_thread.c,v 1.1 2025/11/23 22:01:13 riastradh Exp $");
+
+#include <atf-c.h>
+#include <dlfcn.h>
+#include <pthread.h>
+#include <stdatomic.h>
+#include <unistd.h>
+
+#include "h_macros.h"
+
+pthread_barrier_t bar;
+atomic_bool stop = false;
+
+int sleep_init;
+int sleep_fini;
+int dlopen_cookie;
+int dlclose_cookie;
+
+static const char *const libh_helper_dso[] = {
+ "libh_helper_dso1.so",
+ "libh_helper_dso2.so",
+ "libh_helper_dso3.so",
+};
+
+static void *
+dlclose_thread(void *cookie)
+{
+ const unsigned i = (uintptr_t)cookie % __arraycount(libh_helper_dso);
+ void *handle;
+
+ (void)pthread_barrier_wait(&bar);
+ while (!atomic_load_explicit(&stop, memory_order_relaxed)) {
+ handle = dlopen(libh_helper_dso[i], RTLD_LAZY | RTLD_LOCAL);
+ ATF_REQUIRE(handle != NULL);
+ dlclose(handle);
+ }
+ return NULL;
+}
+
+ATF_TC(dlclose_thread);
+ATF_TC_HEAD(dlclose_thread, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test concurrent dlopen and dlclose with destructors");
+}
+ATF_TC_BODY(dlclose_thread, tc)
+{
+ pthread_t t[2*__arraycount(libh_helper_dso)];
+ unsigned i;
+
+ RZ(pthread_barrier_init(&bar, NULL, 1 + __arraycount(t)));
+ for (i = 0; i < __arraycount(t); i++) {
+ RZ(pthread_create(&t[i], NULL, &dlclose_thread,
+ (void *)(uintptr_t)i));
+ }
+ atf_tc_expect_signal(-1, "PR lib/59751:"
+ " dlclose is not MT-safe depending on the libraries unloaded");
+ (void)pthread_barrier_wait(&bar);
+ (void)sleep(1);
+ atomic_store_explicit(&stop, true, memory_order_relaxed);
+ for (i = 0; i < __arraycount(t); i++)
+ RZ(pthread_join(t[i], NULL));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, dlclose_thread);
+ return atf_no_error();
+}
diff --git a/libexec/ld.elf_so/t_r_rel.sh b/libexec/ld.elf_so/t_r_rel.sh
new file mode 100644
index 000000000000..2bc1059a32ca
--- /dev/null
+++ b/libexec/ld.elf_so/t_r_rel.sh
@@ -0,0 +1,113 @@
+# $NetBSD: t_r_rel.sh,v 1.3 2025/12/21 19:08:09 riastradh Exp $
+#
+# Copyright (c) 2025 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+cleanup_core()
+{
+ local prog
+
+ prog=$1
+ test -f "${prog}.core" || return 0
+ readelf -rs "$(atf_get_srcdir)/${prog}"
+ gdb -batch -ex bt -ex 'info registers' -ex disas \
+ "$(atf_get_srcdir)/${prog}" "${prog}.core"
+}
+
+atf_test_case readelf_relative_nopack
+readelf_relative_nopack_head()
+{
+ atf_set "descr" "readelf R_*_RELATIVE with -z nopack-relative-relocs"
+ atf_set "require.progs" "readelf"
+}
+readelf_relative_nopack_body()
+{
+ atf_check -o match:'R_.*_REL' \
+ readelf -r "$(atf_get_srcdir)"/h_r_rel_nopack
+}
+
+atf_test_case readelf_relative_pack
+readelf_relative_pack_head()
+{
+ atf_set "descr" "readelf R_*_RELATIVE with -z pack-relative-relocs"
+ atf_set "require.progs" "readelf"
+}
+readelf_relative_pack_body()
+{
+ case `uname -p` in
+ i386|powerpc64*|x86_64)
+ ;;
+ *) # Actually missing GNU binutils ld(1) support.
+ atf_expect_fail "PR bin/59360: ld.elf_so(8):" \
+ " missing RELR support"
+ ;;
+ esac
+ atf_check -o not-match:'R_.*_REL' \
+ readelf -r "$(atf_get_srcdir)"/h_r_rel_pack
+}
+
+atf_test_case run_relative_nopack cleanup
+run_relative_nopack_head()
+{
+ atf_set "descr" "run R_*_RELATIVE with -z nopack-relative-relocs"
+}
+run_relative_nopack_body()
+{
+ atf_check "$(atf_get_srcdir)"/h_r_rel_nopack
+}
+run_relative_nopack_cleanup()
+{
+ cleanup_core h_r_rel_nopack
+}
+
+atf_test_case run_relative_pack cleanup
+run_relative_pack_head()
+{
+ atf_set "descr" "run R_*_RELATIVE with -z pack-relative-relocs"
+}
+run_relative_pack_body()
+{
+ case `uname -p` in
+ i386|powerpc64*|x86_64)
+ ;;
+ *) # Missing GNU binutils ld(1) support to generate RELR
+ # sections, so the program should run just fine because
+ # it just uses traditional REL/RELA instead.
+ ;;
+ esac
+ atf_check "$(atf_get_srcdir)"/h_r_rel_pack
+}
+run_relative_pack_cleanup()
+{
+ cleanup_core h_r_rel_pack
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case readelf_relative_nopack
+ atf_add_test_case readelf_relative_pack
+ atf_add_test_case run_relative_nopack
+ atf_add_test_case run_relative_pack
+}
diff --git a/libexec/ld.elf_so/t_tls_alignment.c b/libexec/ld.elf_so/t_tls_alignment.c
new file mode 100644
index 000000000000..13d2a5cf4536
--- /dev/null
+++ b/libexec/ld.elf_so/t_tls_alignment.c
@@ -0,0 +1,70 @@
+/* $NetBSD: t_tls_alignment.c,v 1.2 2026/01/17 10:48:31 skrll Exp $ */
+
+/*
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
+ * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+
+#include <atf-c.h>
+
+#define ALIGNMENT 64
+
+#define MAGIC1 0xaa55aa55aa55aa55
+#define MAGIC2 0xc0ffeeeeeeeeeeee
+#define MAGIC3 0xff00ff00ff00ff00
+
+__thread
+struct {
+ uint64_t magic1;
+ uint64_t magic2 __attribute__((aligned(ALIGNMENT)));
+ uint64_t magic3;
+} tls_data = {
+ .magic1 = MAGIC1,
+ .magic2 = MAGIC2,
+ .magic3 = MAGIC3,
+};
+
+ATF_TC(tls_alignment);
+ATF_TC_HEAD(tls_alignment, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "TLS alignment requirements met");
+}
+
+ATF_TC_BODY(tls_alignment, tc)
+{
+ ATF_CHECK(tls_data.magic1 == MAGIC1);
+ ATF_CHECK(tls_data.magic2 == MAGIC2);
+ ATF_CHECK(tls_data.magic3 == MAGIC3);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, tls_alignment);
+
+ return atf_no_error();
+}
diff --git a/net/if_bridge/broadcast.pcap.uue b/net/if_bridge/broadcast.pcap.uue
new file mode 100644
index 000000000000..a5044e94f7f0
--- /dev/null
+++ b/net/if_bridge/broadcast.pcap.uue
@@ -0,0 +1,5 @@
+begin 644 broadcast.pcap
+MU,.RH0(`!````````````/__```!````0(=S6Q-!#``J````*@````"JJJJJ
+EJO_______P@`10``'``!``!`$:]@P*@`9`H``&0`4`!0``@SS@``
+`
+end
diff --git a/net/if_bridge/unicast.pcap.uue b/net/if_bridge/unicast.pcap.uue
new file mode 100644
index 000000000000..d7c7019272a1
--- /dev/null
+++ b/net/if_bridge/unicast.pcap.uue
@@ -0,0 +1,5 @@
+begin 644 unicast.pcap
+MU,.RH0(`!````````````/__```!````"KIR6R4C!0`J````*@````"JJJJJ
+EJ@`1$1$1$0@`10``'``!``!`$:]@P*@`9`H``&0`4`!0``@SS@``
+`
+end
diff --git a/net/if_shmif/Makefile b/net/if_shmif/Makefile
new file mode 100644
index 000000000000..fe9eb11d4fc1
--- /dev/null
+++ b/net/if_shmif/Makefile
@@ -0,0 +1,13 @@
+# $NetBSD: Makefile,v 1.1 2024/08/20 08:20:19 ozaki-r Exp $
+#
+
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/net/if_shmif
+
+.for name in shmif
+TESTS_SH+= t_${name}
+TESTS_SH_SRC_t_${name}= ../net_common.sh t_${name}.sh
+.endfor
+
+.include <bsd.test.mk>
diff --git a/net/if_shmif/t_shmif.sh b/net/if_shmif/t_shmif.sh
new file mode 100644
index 000000000000..fcd33e357e34
--- /dev/null
+++ b/net/if_shmif/t_shmif.sh
@@ -0,0 +1,138 @@
+# $NetBSD: t_shmif.sh,v 1.1 2024/08/20 08:20:19 ozaki-r Exp $
+#
+# Copyright (c) 2024 Internet Initiative Japan Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+RUMP_SERVER1=unix://./r1
+RUMP_SERVER2=unix://./r2
+
+TIMEOUT=3
+
+atf_test_case shmif_linkstate cleanup
+shmif_linkstate_head()
+{
+ atf_set "descr" "tests of ifconfig media on shmif"
+ atf_set "require.progs" "rump_server"
+}
+
+shmif_linkstate_body()
+{
+ local auto="Ethernet autoselect"
+ local none="Ethernet none"
+
+ rump_server_start $RUMP_SERVER1
+ rump_server_add_iface $RUMP_SERVER1 shmif0 bus1
+
+ export RUMP_SERVER=$RUMP_SERVER1
+ # After ifconfig linkstr, the state becomes UP
+ atf_check -o match:'linkstate: up' \
+ -o match:"media: $auto" \
+ -o not-match:"<UP" rump.ifconfig -v shmif0
+ atf_check rump.ifconfig shmif0 up
+ atf_check -o match:'linkstate: up' \
+ -o match:"media: $auto" \
+ -o match:"<UP" rump.ifconfig -v shmif0
+ # ifconfig media none makes the state DOWN
+ atf_check rump.ifconfig shmif0 media none
+ atf_check -o match:'linkstate: down' \
+ -o match:"media: $none" \
+ -o match:"<UP" rump.ifconfig -v shmif0
+ # ifconfig media auto makes the state UP
+ atf_check rump.ifconfig shmif0 media auto
+ atf_check -o match:'linkstate: up' \
+ -o match:"media: $auto" \
+ -o match:"<UP" rump.ifconfig -v shmif0
+ atf_check rump.ifconfig shmif0 down
+ atf_check -o match:'linkstate: up' \
+ -o match:"media: $auto" \
+ -o not-match:"<UP" rump.ifconfig -v shmif0
+ # After ifconfig -linkstr, the state becomes UNKNOWN
+ atf_check rump.ifconfig shmif0 -linkstr
+ atf_check -o match:'linkstate: unknown' \
+ -o match:"media: $auto" \
+ -o not-match:"<UP" rump.ifconfig -v shmif0
+
+ rump_server_destroy_ifaces
+}
+
+shmif_linkstate_cleanup()
+{
+
+ $DEBUG && dump
+ cleanup
+}
+
+atf_test_case shmif_linkstate_down cleanup
+shmif_linkstate_down_head()
+{
+ atf_set "descr" "tests of behaviors of down shmif"
+ atf_set "require.progs" "rump_server"
+}
+
+shmif_linkstate_down_body()
+{
+
+ rump_server_start $RUMP_SERVER1
+ rump_server_start $RUMP_SERVER2
+ rump_server_add_iface $RUMP_SERVER1 shmif0 bus1
+ rump_server_add_iface $RUMP_SERVER2 shmif0 bus1
+
+ export RUMP_SERVER=$RUMP_SERVER1
+ atf_check rump.sysctl -q -w net.inet.ip.dad_count=0
+ atf_check rump.ifconfig shmif0 10.0.0.1/24 up
+ export RUMP_SERVER=$RUMP_SERVER2
+ atf_check rump.sysctl -q -w net.inet.ip.dad_count=0
+ atf_check rump.ifconfig shmif0 10.0.0.2/24 up
+
+ export RUMP_SERVER=$RUMP_SERVER1
+ atf_check -o ignore rump.ping -c 1 -w $TIMEOUT 10.0.0.2
+
+ atf_check rump.ifconfig shmif0 media none
+ atf_check -o match:'linkstate: down' rump.ifconfig -v shmif0
+
+ # shmif doesn't send any packets on link down
+ atf_check -s not-exit:0 -o ignore rump.ping -c 1 -w $TIMEOUT 10.0.0.2
+
+ atf_check rump.ifconfig shmif0 media auto
+ atf_check -o match:'linkstate: up' rump.ifconfig -v shmif0
+
+ atf_check -o ignore rump.ping -c 1 -w $TIMEOUT 10.0.0.2
+
+ rump_server_destroy_ifaces
+}
+
+shmif_linkstate_down_cleanup()
+{
+
+ $DEBUG && dump
+ cleanup
+}
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case shmif_linkstate
+ atf_add_test_case shmif_linkstate_down
+}
diff --git a/sbin/gpt/gpt.2part.show.label-p b/sbin/gpt/gpt.2part.show.label-p
new file mode 100644
index 000000000000..c25cc3d9229d
--- /dev/null
+++ b/sbin/gpt/gpt.2part.show.label-p
@@ -0,0 +1,7 @@
+0 1 0 PMBR
+1 1 0 Pri GPT header
+2 32 0 Pri GPT table
+34 1024 1 GPT part - potato
+1058 9150 2 GPT part - tomato
+10208 32 0 Sec GPT table
+10240 1 0 Sec GPT header
diff --git a/sbin/gpt/gpt.2part.show.normal-p b/sbin/gpt/gpt.2part.show.normal-p
new file mode 100644
index 000000000000..03a420342c66
--- /dev/null
+++ b/sbin/gpt/gpt.2part.show.normal-p
@@ -0,0 +1,7 @@
+0 1 0 PMBR
+1 1 0 Pri GPT header
+2 32 0 Pri GPT table
+34 1024 1 GPT part - EFI System
+1058 9150 2 GPT part - NetBSD FFSv1/FFSv2
+10208 32 0 Sec GPT table
+10240 1 0 Sec GPT header
diff --git a/sbin/gpt/gpt.2part.show.uuid-p b/sbin/gpt/gpt.2part.show.uuid-p
new file mode 100644
index 000000000000..1315ca228598
--- /dev/null
+++ b/sbin/gpt/gpt.2part.show.uuid-p
@@ -0,0 +1,7 @@
+0 1 0 PMBR
+1 1 0 Pri GPT header
+2 32 0 Pri GPT table
+34 1024 1 GPT part - c12a7328-f81f-11d2-ba4b-00a0c93ec93b
+1058 9150 2 GPT part - 49f48d5a-b10e-11dc-b99b-0019d1879648
+10208 32 0 Sec GPT table
+10240 1 0 Sec GPT header
diff --git a/sbin/gpt/gpt.disklabel.show.normal-p b/sbin/gpt/gpt.disklabel.show.normal-p
new file mode 100644
index 000000000000..b8a389efb2f5
--- /dev/null
+++ b/sbin/gpt/gpt.disklabel.show.normal-p
@@ -0,0 +1,11 @@
+0 1 0 PMBR
+1 1 0 Pri GPT header
+2 32 0 Pri GPT table
+34 29 0 Unused
+63 500 1 GPT part - NetBSD FFSv1/FFSv2
+563 1000 2 GPT part - NetBSD swap
+1563 1000 3 GPT part - NetBSD FFSv1/FFSv2
+2563 400 4 GPT part - Windows basic data
+2963 7245 0 Unused
+10208 32 0 Sec GPT table
+10240 1 0 Sec GPT header
diff --git a/sbin/gpt/gpt.empty.show.clean b/sbin/gpt/gpt.empty.show.clean
new file mode 100644
index 000000000000..8e78dbe04915
--- /dev/null
+++ b/sbin/gpt/gpt.empty.show.clean
@@ -0,0 +1,4 @@
+GPT not found, displaying data from MBR.
+
+ start size index contents
+ 0 10241 Unused
diff --git a/sbin/gpt/gpt.empty.show.clean-p b/sbin/gpt/gpt.empty.show.clean-p
new file mode 100644
index 000000000000..666b5642655e
--- /dev/null
+++ b/sbin/gpt/gpt.empty.show.clean-p
@@ -0,0 +1,3 @@
+GPT not found, displaying data from MBR.
+
+0 10241 0 Unused
diff --git a/sbin/gpt/gpt.empty.show.normal-p b/sbin/gpt/gpt.empty.show.normal-p
new file mode 100644
index 000000000000..2b878d9a4b8b
--- /dev/null
+++ b/sbin/gpt/gpt.empty.show.normal-p
@@ -0,0 +1,6 @@
+0 1 0 PMBR
+1 1 0 Pri GPT header
+2 32 0 Pri GPT table
+34 10174 0 Unused
+10208 32 0 Sec GPT table
+10240 1 0 Sec GPT header
diff --git a/sbin/gpt/gpt.removepart.show.normal-p b/sbin/gpt/gpt.removepart.show.normal-p
new file mode 100644
index 000000000000..1eb369fabfcd
--- /dev/null
+++ b/sbin/gpt/gpt.removepart.show.normal-p
@@ -0,0 +1,7 @@
+0 1 0 PMBR
+1 1 0 Pri GPT header
+2 32 0 Pri GPT table
+34 1024 0 Unused
+1058 9150 2 GPT part - NetBSD FFSv1/FFSv2
+10208 32 0 Sec GPT table
+10240 1 0 Sec GPT header
diff --git a/sbin/gpt/gpt.resizedisk.show.normal-p b/sbin/gpt/gpt.resizedisk.show.normal-p
new file mode 100644
index 000000000000..cfad3e9f4424
--- /dev/null
+++ b/sbin/gpt/gpt.resizedisk.show.normal-p
@@ -0,0 +1,8 @@
+0 1 0 PMBR
+1 1 0 Pri GPT header
+2 32 0 Pri GPT table
+34 1024 1 GPT part - EFI System
+1058 9150 2 GPT part - NetBSD FFSv1/FFSv2
+10208 10240 0 Unused
+20448 32 0 Sec GPT table
+20480 1 0 Sec GPT header
diff --git a/sbin/gpt/gpt.resizepart.show.normal-p b/sbin/gpt/gpt.resizepart.show.normal-p
new file mode 100644
index 000000000000..71b34da592ab
--- /dev/null
+++ b/sbin/gpt/gpt.resizepart.show.normal-p
@@ -0,0 +1,7 @@
+0 1 0 PMBR
+1 1 0 Pri GPT header
+2 32 0 Pri GPT table
+34 1024 1 GPT part - EFI System
+1058 19390 2 GPT part - NetBSD FFSv1/FFSv2
+20448 32 0 Sec GPT table
+20480 1 0 Sec GPT header
diff --git a/usr.bin/cc/t_ctype_abuse.sh b/usr.bin/cc/t_ctype_abuse.sh
new file mode 100644
index 000000000000..e7c7c74d1220
--- /dev/null
+++ b/usr.bin/cc/t_ctype_abuse.sh
@@ -0,0 +1,124 @@
+# $NetBSD: t_ctype_abuse.sh,v 1.1 2024/12/18 02:47:00 riastradh Exp $
+#
+# Copyright (c) 2024 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+ctype_abuse_head()
+{
+ local ctypefn reftype desc
+
+ ctypefn=$1
+ reftype=$2
+
+ case $reftype in
+ var) desc="variable";;
+ ptr) desc="pointer dereference";;
+ array) desc="array element";;
+ funcall)
+ desc="function call";;
+ esac
+
+ atf_set "descr" "Test that $ctypefn warns on $desc of type char"
+ atf_set "require.progs" "cc"
+}
+
+ctype_abuse_body()
+{
+ local ctypefn reftype
+
+ ctypefn=$1
+ reftype=$2
+
+ case $reftype in
+ var) decl='x'; ref='x';;
+ ptr) decl='*x'; ref='*x';;
+ array) decl='x[]'; ref='x[0]';;
+ funcall)
+ decl='f(void)'; ref='f()';;
+ esac
+
+ cat >test.c <<EOF
+#include <ctype.h>
+
+extern char $decl;
+
+int
+g(void)
+{
+
+ return $ctypefn($ref);
+}
+EOF
+ case $reftype in
+ var) atf_expect_fail 'PR lib/58912: ctype(3) abuse detection' \
+ ' fails for variable references';;
+ esac
+ atf_check -s not-exit:0 \
+ -e match:'array subscript has type.*char.*-W.*char-subscripts' \
+ cc -c -Wall -Werror test.c
+}
+
+ctype_abuse_tests()
+{
+ local ctypefn reftype tc
+
+ for ctypefn in \
+ isalpha \
+ isupper \
+ islower \
+ isdigit \
+ isxdigit \
+ isalnum \
+ isspace \
+ ispunct \
+ isprint \
+ isgraph \
+ iscntrl \
+ isblank \
+ toupper \
+ tolower \
+ # end of ctypefn enumeration
+ do
+ for reftype in var ptr array funcall; do
+ tc=${ctypefn}_${reftype}
+ eval "atf_test_case $tc"
+ eval "${tc}_head()
+ {
+ ctype_abuse_head $ctypefn $reftype
+ }"
+ eval "${tc}_body()
+ {
+ ctype_abuse_body $ctypefn $reftype
+ }"
+ atf_add_test_case $tc
+ done
+ done
+}
+
+atf_init_test_cases()
+{
+
+ ctype_abuse_tests
+}
diff --git a/usr.bin/cc/t_libm_cabs.sh b/usr.bin/cc/t_libm_cabs.sh
new file mode 100644
index 000000000000..85cce3f19228
--- /dev/null
+++ b/usr.bin/cc/t_libm_cabs.sh
@@ -0,0 +1,57 @@
+# $NetBSD: t_libm_cabs.sh,v 1.1 2026/01/27 20:01:47 mrg Exp $
+#
+# Copyright (c) 2026 Matthew R. Green
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+
+atf_test_case libm_cabs
+libm_cabs_head() {
+ atf_set "descr" "compile using __builtin_cabsl(3) which should be renamed"
+ atf_set "require.progs" "cc"
+}
+
+#
+# Simple program that misses including <complex.h> so that the renames
+# GCC itself is supposed to be doing are also applied, which makes their
+# uses in libgfortran use the correct symbols. The use of
+# "__builtin_cabsl" should become "__c99_cabsl".
+#
+libm_cabs_body() {
+ cat > cabsl.c << EOF
+long double my_foo(long double _Complex);
+long double
+my_foo(long double _Complex z)
+{
+ return __builtin_cabsl(z);
+}
+EOF
+ atf_check -s exit:0 -o ignore -e ignore cc -O3 -c cabsl.c
+ atf_check -s exit:0 -o match:__c99_cabsl -e empty objdump -dr cabsl.o
+}
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case libm_cabs
+}
diff --git a/usr.bin/cc/t_pthread_abuse.sh b/usr.bin/cc/t_pthread_abuse.sh
new file mode 100644
index 000000000000..caf88e60557f
--- /dev/null
+++ b/usr.bin/cc/t_pthread_abuse.sh
@@ -0,0 +1,79 @@
+# $NetBSD: t_pthread_abuse.sh,v 1.1 2025/10/06 13:11:56 riastradh Exp $
+#
+# Copyright (c) 2025 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+pthread_abuse_head()
+{
+
+ atf_set "descr" \
+ "Test that pthread_create calls without -lpthread fail to link"
+ atf_set "require.progs" "cc"
+}
+pthread_abuse_body()
+{
+
+ cat >test.c <<'EOF'
+#include <err.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+static void *
+start(void *cookie)
+{
+ return cookie;
+}
+
+int
+main(void)
+{
+ int cookie = 123;
+ pthread_t t;
+ void *result;
+ int error;
+
+ error = pthread_create(&t, NULL, &start, &cookie);
+ if (error)
+ errc(EXIT_FAILURE, error, "pthread_create");
+ error = pthread_join(t, &result);
+ if (error)
+ errc(EXIT_FAILURE, error, "pthread_join");
+ return (result == &cookie ? 0 : EXIT_FAILURE);
+}
+EOF
+ atf_check -s not-exit:0 \
+ -e match:'undefined reference to.*pthread_create' \
+ cc -o test test.c
+ atf_check cc -o test test.c -lpthread
+ atf_check ./test
+ atf_check cc -o test test.c -pthread
+ atf_check ./test
+}
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case pthread_abuse
+}
diff --git a/usr.bin/diff/functionname.in b/usr.bin/diff/functionname.in
new file mode 100644
index 000000000000..7b4c50c86cd9
--- /dev/null
+++ b/usr.bin/diff/functionname.in
@@ -0,0 +1,29 @@
+static void
+doSomethingThenPrintHello(int test)
+{
+ test = test << 4;
+ if (test % 8 == 6) {
+ return;
+ }
+
+ print("goodbye\n");
+}
+
+
+- (long) readOffset:(FILE*)file
+{
+ if( version >= 11){
+ long offset;
+ fread(&offset, sizeof(long), 1, file);
+ return offset;
+ } else {
+ int offset;
+ fread(&offset, sizeof(int), 1, file);
+ return offset;
+ }
+}
+
++ (BOOL) isEdible:(NSString *)mushroom
+{
+ return TRUE;
+}
diff --git a/usr.bin/diff/functionname_c.in b/usr.bin/diff/functionname_c.in
new file mode 100644
index 000000000000..84f6846783ca
--- /dev/null
+++ b/usr.bin/diff/functionname_c.in
@@ -0,0 +1,29 @@
+static void
+doSomethingThenPrintHello(int test)
+{
+ test = test << 4;
+ if (test % 8 == 6) {
+ return;
+ }
+
+ print("hello\n");
+}
+
+
+- (long) readOffset:(FILE*)file
+{
+ if( version >= 11){
+ long offset;
+ fread(&offset, sizeof(long), 1, file);
+ return offset;
+ } else {
+ int offset;
+ fread(&offset, sizeof(int), 1, file);
+ return offset;
+ }
+}
+
++ (BOOL) isEdible:(NSString *)mushroom
+{
+ return TRUE;
+}
diff --git a/usr.bin/diff/functionname_c.out b/usr.bin/diff/functionname_c.out
new file mode 100644
index 000000000000..b17ce05d04ca
--- /dev/null
+++ b/usr.bin/diff/functionname_c.out
@@ -0,0 +1,11 @@
+--- functionname.in
++++ functionname_c.in
+@@ -6,7 +6,7 @@ doSomethingThenPrintHello(int test)
+ return;
+ }
+
+- print("goodbye\n");
++ print("hello\n");
+ }
+
+
diff --git a/usr.bin/diff/header.out b/usr.bin/diff/header.out
new file mode 100644
index 000000000000..2e1665a30e6d
--- /dev/null
+++ b/usr.bin/diff/header.out
@@ -0,0 +1,4 @@
+--- empty 2015-04-03 01:02:03.000000000 +0000
++++ hello 2016-12-22 11:22:33.000000000 +0000
+@@ -0,0 +1 @@
++hello
diff --git a/usr.bin/diff/header_ns.out b/usr.bin/diff/header_ns.out
new file mode 100644
index 000000000000..b1316dfc12b9
--- /dev/null
+++ b/usr.bin/diff/header_ns.out
@@ -0,0 +1,4 @@
+--- empty 2015-04-03 01:02:03.123456789 +0000
++++ hello 2016-12-22 11:22:33.987654321 +0000
+@@ -0,0 +1 @@
++hello
diff --git a/usr.bin/diff/input1.in b/usr.bin/diff/input1.in
new file mode 100644
index 000000000000..3892e8400f86
--- /dev/null
+++ b/usr.bin/diff/input1.in
@@ -0,0 +1,2 @@
+Simple input file designed
+to be able to test diff
diff --git a/usr.bin/diff/input2.in b/usr.bin/diff/input2.in
new file mode 100644
index 000000000000..c38b487353a7
--- /dev/null
+++ b/usr.bin/diff/input2.in
@@ -0,0 +1,3 @@
+Simple input file designed
+and written
+to be able to test diff utility
diff --git a/usr.bin/diff/input_c1.in b/usr.bin/diff/input_c1.in
new file mode 100644
index 000000000000..d39dfbdc511b
--- /dev/null
+++ b/usr.bin/diff/input_c1.in
@@ -0,0 +1,15 @@
+/*
+ * A comment
+ *
+ * And another bla
+ *
+ * And yet another
+ */
+
+int
+main(void)
+{
+ printf("something");
+
+ return (0);
+}
diff --git a/usr.bin/diff/input_c2.in b/usr.bin/diff/input_c2.in
new file mode 100644
index 000000000000..933ec67dc175
--- /dev/null
+++ b/usr.bin/diff/input_c2.in
@@ -0,0 +1,16 @@
+/*
+ * A comment
+ *
+ * And another bla
+ *
+ * and yet another
+ */
+
+int
+main(void)
+{
+
+ printf("something");
+
+ return (0);
+}
diff --git a/usr.bin/diff/simple.out b/usr.bin/diff/simple.out
new file mode 100644
index 000000000000..fcbcaa041e8c
--- /dev/null
+++ b/usr.bin/diff/simple.out
@@ -0,0 +1,5 @@
+2c2,3
+< to be able to test diff
+---
+> and written
+> to be able to test diff utility
diff --git a/usr.bin/diff/simple_b.out b/usr.bin/diff/simple_b.out
new file mode 100644
index 000000000000..704be9d621a8
--- /dev/null
+++ b/usr.bin/diff/simple_b.out
@@ -0,0 +1,6 @@
+6c6
+< * And yet another
+---
+> * and yet another
+11a12
+>
diff --git a/usr.bin/diff/simple_e.out b/usr.bin/diff/simple_e.out
new file mode 100644
index 000000000000..0c7e2b5c752b
--- /dev/null
+++ b/usr.bin/diff/simple_e.out
@@ -0,0 +1,4 @@
+2c
+and written
+to be able to test diff utility
+.
diff --git a/usr.bin/diff/simple_i.out b/usr.bin/diff/simple_i.out
new file mode 100644
index 000000000000..9edc1f98d72d
--- /dev/null
+++ b/usr.bin/diff/simple_i.out
@@ -0,0 +1,6 @@
+4c4
+< * And another bla
+---
+> * And another bla
+11a12
+>
diff --git a/usr.bin/diff/simple_n.out b/usr.bin/diff/simple_n.out
new file mode 100644
index 000000000000..33ca7090cf97
--- /dev/null
+++ b/usr.bin/diff/simple_n.out
@@ -0,0 +1,4 @@
+d2 1
+a2 2
+and written
+to be able to test diff utility
diff --git a/usr.bin/diff/simple_p.out b/usr.bin/diff/simple_p.out
new file mode 100644
index 000000000000..f5aebb0d1199
--- /dev/null
+++ b/usr.bin/diff/simple_p.out
@@ -0,0 +1,34 @@
+*** input_c1.in
+--- input_c2.in
+***************
+*** 1,14 ****
+ /*
+ * A comment
+ *
+! * And another bla
+ *
+! * And yet another
+ */
+
+ int
+ main(void)
+ {
+ printf("something");
+
+ return (0);
+--- 1,15 ----
+ /*
+ * A comment
+ *
+! * And another bla
+ *
+! * and yet another
+ */
+
+ int
+ main(void)
+ {
++
+ printf("something");
+
+ return (0);
diff --git a/usr.bin/diff/simple_u.out b/usr.bin/diff/simple_u.out
new file mode 100644
index 000000000000..f341987ebec6
--- /dev/null
+++ b/usr.bin/diff/simple_u.out
@@ -0,0 +1,7 @@
+--- input1
++++ input2
+@@ -1,2 +1,3 @@
+ Simple input file designed
+-to be able to test diff
++and written
++to be able to test diff utility
diff --git a/usr.bin/diff/simple_w.out b/usr.bin/diff/simple_w.out
new file mode 100644
index 000000000000..704be9d621a8
--- /dev/null
+++ b/usr.bin/diff/simple_w.out
@@ -0,0 +1,6 @@
+6c6
+< * And yet another
+---
+> * and yet another
+11a12
+>
diff --git a/usr.bin/diff/unified_9999.out b/usr.bin/diff/unified_9999.out
new file mode 100644
index 000000000000..0f9303fbdc7c
--- /dev/null
+++ b/usr.bin/diff/unified_9999.out
@@ -0,0 +1,21 @@
+--- input_c1.in
++++ input_c2.in
+@@ -1,15 +1,16 @@
+ /*
+ * A comment
+ *
+- * And another bla
++ * And another bla
+ *
+- * And yet another
++ * and yet another
+ */
+
+ int
+ main(void)
+ {
++
+ printf("something");
+
+ return (0);
+ }
diff --git a/usr.bin/diff/unified_p.out b/usr.bin/diff/unified_p.out
new file mode 100644
index 000000000000..938b07890fbc
--- /dev/null
+++ b/usr.bin/diff/unified_p.out
@@ -0,0 +1,20 @@
+--- input_c1.in
++++ input_c2.in
+@@ -1,14 +1,15 @@
+ /*
+ * A comment
+ *
+- * And another bla
++ * And another bla
+ *
+- * And yet another
++ * and yet another
+ */
+
+ int
+ main(void)
+ {
++
+ printf("something");
+
+ return (0);
diff --git a/usr.bin/error/Makefile b/usr.bin/error/Makefile
new file mode 100644
index 000000000000..6d3e2e72fbc9
--- /dev/null
+++ b/usr.bin/error/Makefile
@@ -0,0 +1,6 @@
+# $NetBSD: Makefile,v 1.1 2023/08/26 10:06:16 rillig Exp $
+
+TESTSDIR= ${TESTSBASE}/usr.bin/error
+TESTS_SH= t_error
+
+.include <bsd.test.mk>
diff --git a/usr.bin/error/t_error.sh b/usr.bin/error/t_error.sh
new file mode 100644
index 000000000000..89f25e90b4e4
--- /dev/null
+++ b/usr.bin/error/t_error.sh
@@ -0,0 +1,101 @@
+# $NetBSD: t_error.sh,v 1.1 2023/08/26 10:06:16 rillig Exp $
+#
+# Copyright (c) 2023 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# This code is derived from software contributed to The NetBSD Foundation
+# by Roland Illig.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+create_file() {
+ local fn="$1"; shift
+ printf '%s\n' "$@" > "$fn"
+}
+
+atf_test_case cc
+cc_body() {
+ create_file code.c \
+ 'goto error'
+ create_file err \
+ 'code.c:1: error: syntax error'
+ create_file expected \
+ '/*###1 [cc] error: syntax error%%%*/' \
+ 'goto error'
+
+ atf_check -o ignore \
+ error err
+ atf_check -o 'file:expected' cat code.c
+}
+
+atf_test_case f77
+f77_body() {
+ create_file code.f \
+ 'doi=1,1,1'
+ create_file err \
+ 'Compiler error line 1 of code.f: syntax error'
+ create_file expected \
+ 'C###1 [f77] Compiler error line 1 of code.f syntax error%%%' \
+ 'doi=1,1,1'
+
+ atf_check -o ignore \
+ error err
+ atf_check -o 'file:expected' cat code.f
+}
+
+atf_test_case lint
+lint_body() {
+ create_file code.c \
+ 'goto error'
+ create_file err \
+ 'code.c(1): syntax error'
+ create_file expected \
+ '/*###1 [lint] syntax error%%%*/' \
+ 'goto error'
+
+ atf_check -o ignore \
+ error err
+ atf_check -o 'file:expected' cat code.c
+}
+
+atf_test_case mod2
+mod2_body() {
+ create_file code.m2 \
+ 'END.'
+ create_file err \
+ 'File code.m2, line 1: missing BEGIN'
+ create_file expected \
+ '(*###1 [mod2] missing BEGIN%%%*)' \
+ 'END.'
+
+ atf_check -o ignore \
+ error err
+ atf_check -o 'file:expected' cat code.m2
+}
+
+atf_init_test_cases() {
+ atf_add_test_case cc
+ atf_add_test_case f77
+ atf_add_test_case lint
+ atf_add_test_case mod2
+}
diff --git a/usr.bin/ftp/Makefile b/usr.bin/ftp/Makefile
new file mode 100644
index 000000000000..8d47e55379fc
--- /dev/null
+++ b/usr.bin/ftp/Makefile
@@ -0,0 +1,15 @@
+# $NetBSD: Makefile,v 1.1 2024/10/12 22:19:37 riastradh Exp $
+
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/usr.bin/ftp
+TESTS_SH= t_custom_headers
+
+SCRIPTSDIR= ${TESTSDIR}
+SCRIPTS+= custom_headers.sh
+
+# Keep the .sh suffix because we use it to trigger cgi-bin handling in
+# bozohttpd (rather silly but it's easier that way).
+SCRIPTSNAME_custom_headers.sh= custom_headers.sh
+
+.include <bsd.test.mk>
diff --git a/usr.bin/ftp/custom_headers.sh b/usr.bin/ftp/custom_headers.sh
new file mode 100644
index 000000000000..e364ef0e784e
--- /dev/null
+++ b/usr.bin/ftp/custom_headers.sh
@@ -0,0 +1,36 @@
+#! /bin/sh
+#
+# $NetBSD: custom_headers.sh,v 1.1 2024/10/12 22:19:37 riastradh Exp $
+#
+# Copyright (c) 2024 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# This code is derived from software contributed to The NetBSD Foundation
+# by Sunil Nimmagadda.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+echo "Content-type: text/plain; charset=utf-8"
+echo ""
+echo "HTTP_X_ORIGIN=$HTTP_X_ORIGIN"
+echo "HTTP_X_RATE_LIMIT=$HTTP_X_RATE_LIMIT"
diff --git a/usr.bin/ftp/t_custom_headers.sh b/usr.bin/ftp/t_custom_headers.sh
new file mode 100644
index 000000000000..bdf185512f68
--- /dev/null
+++ b/usr.bin/ftp/t_custom_headers.sh
@@ -0,0 +1,67 @@
+# $NetBSD: t_custom_headers.sh,v 1.1 2024/10/12 22:19:37 riastradh Exp $
+#
+# Copyright (c) 2024 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# This code is derived from software contributed to The NetBSD Foundation
+# by Sunil Nimmagadda.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+atf_test_case custom_headers cleanup
+custom_headers_head()
+{
+ atf_require_prog ftp
+ atf_set "descr" "Check for custom HTTP headers"
+}
+
+HTTPD_PID=./.__httpd.pid
+custom_headers_body()
+{
+ # start httpd in daemon mode
+ atf_check -s exit:0 \
+ /usr/libexec/httpd -P $HTTPD_PID -I 8080 -b -C .sh /bin/sh \
+ -c "$(atf_get_srcdir)" .
+
+ atf_check \
+ -o inline:'HTTP_X_ORIGIN=example.com\nHTTP_X_RATE_LIMIT=1000\n' \
+ ftp -V -o - \
+ -H 'X-Origin: example.com' \
+ -H 'X-Rate-Limit: 1000' \
+ http://127.0.0.1:8080/cgi-bin/custom_headers.sh
+}
+
+custom_headers_cleanup()
+{
+ if [ -f "$HTTPD_PID" ]; then
+ echo kill -9 "$(cat $HTTPD_PID)"
+ kill -9 "$(cat $HTTPD_PID)"
+ echo '# wait for httpd to exit'
+ sleep 1
+ fi
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case custom_headers
+}
diff --git a/usr.bin/gcov/Makefile b/usr.bin/gcov/Makefile
new file mode 100644
index 000000000000..6828aac4b00e
--- /dev/null
+++ b/usr.bin/gcov/Makefile
@@ -0,0 +1,7 @@
+# $NetBSD: Makefile,v 1.1 2025/01/18 22:31:22 rillig Exp $
+
+TESTSDIR= ${TESTSBASE}/usr.bin/gcov
+
+TESTS_SH= t_gcov
+
+.include <bsd.test.mk>
diff --git a/usr.bin/gcov/t_gcov.sh b/usr.bin/gcov/t_gcov.sh
new file mode 100644
index 000000000000..7895a9b9adf0
--- /dev/null
+++ b/usr.bin/gcov/t_gcov.sh
@@ -0,0 +1,104 @@
+# $NetBSD: t_gcov.sh,v 1.1 2025/01/18 22:31:22 rillig Exp $
+#
+# Copyright (c) 2025 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+after_exec_head()
+{
+}
+after_exec_body()
+{
+ atf_require_prog cat
+ atf_require_prog gcc
+ atf_require_prog gcov
+ atf_require_prog grep
+
+ cat <<EOF >prog.c
+#include <unistd.h>
+
+int
+main(void)
+{
+ pid_t pid = vfork();
+ switch (pid) {
+ case 0:
+ execl("/bin/sh", "sh", "-c", ":", (const char *)0);
+ /* FALLTHROUGH */
+ case -1:
+ write(2, "error\n", 6);
+ _exit(1);
+ }
+
+ write(1, "reached\n", 8);
+ return 0;
+}
+EOF
+
+ cat <<EOF >prog.c.gcov.expected
+ -: 0:Source:prog.c
+ -: 0:Graph:prog.gcno
+ -: 0:Data:prog.gcda
+ -: 0:Runs:1
+ -: 1:#include <unistd.h>
+ -: 2:
+ -: 3:int
+ 1: 4:main(void)
+ -: 5:{
+ 1: 6: pid_t pid = vfork();
+ 1: 7: switch (pid) {
+ 1: 8: case 0:
+ 1: 9: execl("/bin/sh", "sh", "-c", ":", (const char *)0);
+ -: 10: /* FALLTHROUGH */
+ #####: 11: case -1:
+ #####: 12: write(2, "error\n", 6);
+ #####: 13: _exit(1);
+ -: 14: }
+ -: 15:
+ #####: 16: write(1, "reached\n", 8);
+ #####: 17: return 0;
+ -: 18:}
+EOF
+
+ atf_check \
+ gcc --coverage -c prog.c
+ atf_check \
+ gcc --coverage -o prog prog.o
+ atf_check -o inline:'reached\n' \
+ ./prog
+ atf_check -o ignore \
+ gcov prog.c
+
+ atf_check -o file:prog.c.gcov.expected \
+ cat prog.c.gcov
+
+ # FIXME: The code was reached once but is reported as unreached.
+ atf_check -o ignore \
+ grep "#####.*reached" prog.c.gcov
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case after_exec
+}
diff --git a/usr.bin/mtree/Makefile b/usr.bin/mtree/Makefile
new file mode 100644
index 000000000000..a7f091f3832f
--- /dev/null
+++ b/usr.bin/mtree/Makefile
@@ -0,0 +1,8 @@
+# $NetBSD: Makefile,v 1.1 2024/01/25 00:30:57 riastradh Exp $
+#
+
+TESTSDIR= ${TESTSBASE}/usr.bin/mtree
+
+TESTS_SH+= t_sets
+
+.include <bsd.test.mk>
diff --git a/usr.bin/mtree/t_sets.sh b/usr.bin/mtree/t_sets.sh
new file mode 100644
index 000000000000..c9f56956bfdd
--- /dev/null
+++ b/usr.bin/mtree/t_sets.sh
@@ -0,0 +1,126 @@
+# $NetBSD: t_sets.sh,v 1.9 2024/05/10 03:29:47 riastradh Exp $
+#
+# Copyright (c) 2024 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+check_mtree()
+{
+ local set=$1
+
+ cd /
+ atf_check -o empty -s exit:0 \
+ mtree -e </etc/mtree/set."$set"
+}
+
+set_case()
+{
+ local set=$1
+
+ eval "set_${set}_head() { atf_set descr \"/etc/mtree/set.${set}\"; }"
+ eval "set_${set}_body() { check_mtree ${set}; }"
+ eval "set_${set}_defined="
+}
+
+set_case base
+set_case base32
+set_case base64
+set_case comp
+set_case debug
+set_case debug32
+set_case debug64
+set_case dtb
+#set_case etc
+set_case games
+set_case gpufw
+set_case man
+set_case manhtml
+set_case misc
+set_case modules
+set_case rescue
+set_case tests
+set_case text
+set_case xbase
+set_case xcomp
+set_case xdebug
+#set_case xetc
+set_case xfont
+set_case xserver
+
+sets_unknown=
+
+sets_unknown_head()
+{
+ atf_set descr "Verify this tests lists all sets"
+}
+sets_unknown_body()
+{
+ test -z "$sets_unknown" || atf_fail "Unknown sets: ${sets_unknown}"
+}
+
+atf_init_test_cases()
+{
+ local mtree set defined
+
+ atf_add_test_case sets_unknown
+
+ # base is always installed -- hard-code this in case we make a
+ # mistake with the automatic set detection.
+ atf_add_test_case set_base
+
+ # Test all of the sets that are installed, except for some
+ # special cases.
+ for mtree in /etc/mtree/set.*; do
+ set=${mtree#/etc/mtree/set.}
+ case $set in
+ base) # Handled above already.
+ continue
+ ;;
+ dtb)
+ # contents of this set go to the boot partition,
+ # which may not be mounted during normal operation
+ if [ ! -d /boot/dtb ]; then
+ continue;
+ fi
+ ;;
+ etc|xetc)
+ # etc and xetc have files that may be modified
+ # on installation, and also contain log files,
+ # so let's skip them for now.
+ continue
+ ;;
+ *) ;;
+ esac
+
+ # If we have a test for this set, add it. Otherwise,
+ # add it to the unknown list to make the test suite
+ # fail.
+ eval 'defined=${set_'"$set"'_defined+yes}'
+ if [ -n "$defined" ]; then
+ atf_add_test_case set_"${set}"
+ else
+ sets_unknown="${sets_unknown}${sets_unknown:+ }${set}"
+ fi
+ done
+}
diff --git a/usr.bin/netpgpkeys/Makefile b/usr.bin/netpgpkeys/Makefile
new file mode 100644
index 000000000000..7852906c3ee7
--- /dev/null
+++ b/usr.bin/netpgpkeys/Makefile
@@ -0,0 +1,13 @@
+# $NetBSD: Makefile,v 1.2 2026/01/29 16:14:45 wiz Exp $
+
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/usr.bin/netpgpkeys
+
+TESTS_SH+= t_netpgpkeys
+
+FILESDIR= ${TESTSDIR}/data
+FILES+= data/testkey-ec.pub
+FILES+= data/testkey-rsa.pub
+
+.include <bsd.test.mk>
diff --git a/usr.bin/netpgpkeys/data/testkey-ec.pub b/usr.bin/netpgpkeys/data/testkey-ec.pub
new file mode 100644
index 000000000000..aff1af8376da
--- /dev/null
+++ b/usr.bin/netpgpkeys/data/testkey-ec.pub
Binary files differ
diff --git a/usr.bin/netpgpkeys/data/testkey-rsa.pub b/usr.bin/netpgpkeys/data/testkey-rsa.pub
new file mode 100644
index 000000000000..d4bec383e5ea
--- /dev/null
+++ b/usr.bin/netpgpkeys/data/testkey-rsa.pub
Binary files differ
diff --git a/usr.bin/netpgpkeys/t_netpgpkeys.sh b/usr.bin/netpgpkeys/t_netpgpkeys.sh
new file mode 100644
index 000000000000..08d01a7fe98e
--- /dev/null
+++ b/usr.bin/netpgpkeys/t_netpgpkeys.sh
@@ -0,0 +1,81 @@
+#! /bin/sh
+
+# $NetBSD: t_netpgpkeys.sh,v 1.3 2026/01/31 22:35:10 wiz Exp $
+
+#
+# Copyright (c) 2026 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# This code is derived from software contributed to The NetBSD Foundation
+# by Thomas Klausner <wiz@NetBSD.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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+cleanup_core() {
+ local prog
+
+ prog=$1
+ test -f "${prog}.core" || return 0
+ readelf -rs "/usr/bin/${prog}"
+ gdb -batch -ex bt -ex 'info registers' -ex disas \
+ "/usr/bin/${prog}" "${prog}.core"
+}
+
+# Test set 1 (rsa key) for netpgpkeys
+atf_test_case netpgpkeys_testset_1_rsa_keys
+
+netpgpkeys_testset_1_rsa_keys_head() {
+ atf_set "descr" "Test set 1 (rsa_keys) for netpgpkeys"
+}
+netpgpkeys_testset_1_rsa_keys_body() {
+ atf_expect_fail "PR bin/59936 - does not support keys generated by gnugp2"
+ # TODO: fix netpgpkeys so it doesn't need an empty keyring
+ atf_check touch pubring.gpg
+ atf_check netpgpkeys --keyring pubring.gpg \
+ --import-key "$(atf_get_srcdir)"/data/testkey-rsa.pub
+}
+
+# Test set 2 (elliptic curve keys) for netpgpkeys
+atf_test_case netpgpkeys_testset_2_ec_keys cleanup
+
+netpgpkeys_testset_2_ec_keys_head() {
+ atf_set "descr" "Test set 2 (ec_keys) for netpgpkeys"
+}
+netpgpkeys_testset_2_ec_keys_body() {
+ atf_expect_fail "PR bin/59936 - does not support keys generated by gnugp2 - dumps core for EC keys"
+ # TODO: fix netpgpkeys so it doesn't need an empty keyring
+ atf_check touch pubring.gpg
+ atf_check netpgpkeys --keyring pubring.gpg \
+ --import-key "$(atf_get_srcdir)"/data/testkey-ec.pub \
+ --coredumps
+}
+
+netpgpkeys_testset_2_ec_keys_cleanup() {
+ cleanup_core netpgpkeys
+}
+
+# all test sets
+atf_init_test_cases() {
+ atf_add_test_case netpgpkeys_testset_1_rsa_keys
+ atf_add_test_case netpgpkeys_testset_2_ec_keys
+}
diff --git a/usr.bin/netpgpverify/data/D5B22A28.pub b/usr.bin/netpgpverify/data/D5B22A28.pub
new file mode 100644
index 000000000000..3e41c990e290
--- /dev/null
+++ b/usr.bin/netpgpverify/data/D5B22A28.pub
Binary files differ
diff --git a/usr.bin/netpgpverify/data/D5B22A28.secret b/usr.bin/netpgpverify/data/D5B22A28.secret
new file mode 100644
index 000000000000..2c9f1fb35b72
--- /dev/null
+++ b/usr.bin/netpgpverify/data/D5B22A28.secret
Binary files differ
diff --git a/usr.bin/netpgpverify/data/NetBSD-6.0_hashes.asc.gz b/usr.bin/netpgpverify/data/NetBSD-6.0_hashes.asc.gz
new file mode 100644
index 000000000000..b1499aaeb80e
--- /dev/null
+++ b/usr.bin/netpgpverify/data/NetBSD-6.0_hashes.asc.gz
Binary files differ
diff --git a/usr.bin/netpgpverify/data/a.gpg b/usr.bin/netpgpverify/data/a.gpg
new file mode 100644
index 000000000000..4041a0aa61b0
--- /dev/null
+++ b/usr.bin/netpgpverify/data/a.gpg
Binary files differ
diff --git a/usr.bin/netpgpverify/data/b.gpg b/usr.bin/netpgpverify/data/b.gpg
new file mode 100644
index 000000000000..6934bddc429a
--- /dev/null
+++ b/usr.bin/netpgpverify/data/b.gpg
Binary files differ
diff --git a/usr.bin/netpgpverify/data/det b/usr.bin/netpgpverify/data/det
new file mode 100644
index 000000000000..04dc803533a2
--- /dev/null
+++ b/usr.bin/netpgpverify/data/det
@@ -0,0 +1,16 @@
+To Do
+=====
+tests with -k sig
+detached sigs
+DSA
+
+Done
+====
+basics
+localise pgp_read_packets
+fix lint
+WARNS=5
+lib man page
+prog man page
+do we do it statically linked as well?
+multiple files in netpgpverify
diff --git a/usr.bin/netpgpverify/data/det.sig b/usr.bin/netpgpverify/data/det.sig
new file mode 100644
index 000000000000..5bf66e7878cf
--- /dev/null
+++ b/usr.bin/netpgpverify/data/det.sig
Binary files differ
diff --git a/usr.bin/netpgpverify/data/dsa-pubring.gpg b/usr.bin/netpgpverify/data/dsa-pubring.gpg
new file mode 100644
index 000000000000..a941a8fa5ee5
--- /dev/null
+++ b/usr.bin/netpgpverify/data/dsa-pubring.gpg
Binary files differ
diff --git a/usr.bin/netpgpverify/data/expected16 b/usr.bin/netpgpverify/data/expected16
new file mode 100644
index 000000000000..958c7b960389
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected16
@@ -0,0 +1,8 @@
+Good signature for b.gpg made Mon Sep 10 00:15:38 2012
+signature 2048/RSA (Encrypt or Sign) 1b68dcfcc0596823 2004-01-12
+fingerprint d415 9deb 336d e4cc cdfa 00cd 1b68 dcfc c059 6823
+uid Alistair Crooks <agc@alistaircrooks.com>
+uid Alistair Crooks <agc@pkgsrc.org>
+uid Alistair Crooks <agc@netbsd.org>
+uid Alistair Crooks <agc@netflix.com>
+
diff --git a/usr.bin/netpgpverify/data/expected17 b/usr.bin/netpgpverify/data/expected17
new file mode 100644
index 000000000000..0ea49e043f65
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected17
@@ -0,0 +1,10 @@
+Good signature for a.gpg made Sun Sep 9 17:44:11 2012
+signature 2048/RSA (Encrypt or Sign) 1b68dcfcc0596823 2004-01-12
+fingerprint: d415 9deb 336d e4cc cdfa 00cd 1b68 dcfc c059 6823
+uid Alistair Crooks <agc@alistaircrooks.com>
+uid Alistair Crooks <agc@pkgsrc.org>
+uid Alistair Crooks <agc@netbsd.org>
+uid Alistair Crooks <agc@netflix.com>
+encryption 2048/RSA (Encrypt or Sign) 79deb61e488eee74 2004-01-12
+fingerprint: 57c0 c1e6 bf71 8845 416b 9522 79de b61e 488e ee74
+
diff --git a/usr.bin/netpgpverify/data/expected18 b/usr.bin/netpgpverify/data/expected18
new file mode 100644
index 000000000000..00bfcb1c193e
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected18
@@ -0,0 +1,8 @@
+Good signature for a.gpg made Tue May 31 23:29:10 2016
+signature 2048/RSA (Encrypt or Sign) 1b68dcfcc0596823 2004-01-12
+fingerprint d415 9deb 336d e4cc cdfa 00cd 1b68 dcfc c059 6823
+uid Alistair Crooks <agc@alistaircrooks.com>
+uid Alistair Crooks <agc@pkgsrc.org>
+uid Alistair Crooks <agc@netbsd.org>
+uid Alistair Crooks <agc@netflix.com>
+
diff --git a/usr.bin/netpgpverify/data/expected19 b/usr.bin/netpgpverify/data/expected19
new file mode 100644
index 000000000000..eb96b974c4cd
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected19
@@ -0,0 +1,7 @@
+Good signature for NetBSD-6.0_RC2_hashes.asc made Wed Sep 19 07:53:18 2012
+signature 4096/RSA (Encrypt or Sign) 064973ac4c4a706e 2009-06-23
+fingerprint: ddee 2bdb 9c98 a0d1 d4fb dbf7 0649 73ac 4c4a 706e
+uid NetBSD Security Officer <security-officer@NetBSD.org>
+encryption 4096/RSA (Encrypt or Sign) 9ff2c24fdf2ce620 2009-06-23 [Expiry 2019-06-21]
+fingerprint: 1915 0801 fbd8 f45d 89f2 0205 9ff2 c24f df2c e620
+
diff --git a/usr.bin/netpgpverify/data/expected20 b/usr.bin/netpgpverify/data/expected20
new file mode 100644
index 000000000000..4715faa78887
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected20
@@ -0,0 +1,18 @@
+1. tag & 0x3f
+2. len
+
+one pass (tag 4)
+========
+b version:3
+b sig type
+b hash alg
+b pubkey alg
+8b keyid
+
+literal data (tag 11)
+=============
+b binary/text
+b length
+c string
+L mtime
+text
diff --git a/usr.bin/netpgpverify/data/expected21 b/usr.bin/netpgpverify/data/expected21
new file mode 100644
index 000000000000..4ae923b07724
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected21
@@ -0,0 +1,8 @@
+Good signature for [stdin] made Tue May 31 23:29:10 2016
+signature 2048/RSA (Encrypt or Sign) 1b68dcfcc0596823 2004-01-12
+fingerprint d415 9deb 336d e4cc cdfa 00cd 1b68 dcfc c059 6823
+uid Alistair Crooks <agc@alistaircrooks.com>
+uid Alistair Crooks <agc@pkgsrc.org>
+uid Alistair Crooks <agc@netbsd.org>
+uid Alistair Crooks <agc@netflix.com>
+
diff --git a/usr.bin/netpgpverify/data/expected22 b/usr.bin/netpgpverify/data/expected22
new file mode 100644
index 000000000000..e9da07566c77
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected22
@@ -0,0 +1,8 @@
+Good signature for [stdin] made Sun Sep 30 10:50:20 2012
+signature 2048/RSA (Encrypt or Sign) 1b68dcfcc0596823 2004-01-12
+fingerprint d415 9deb 336d e4cc cdfa 00cd 1b68 dcfc c059 6823
+uid Alistair Crooks <agc@alistaircrooks.com>
+uid Alistair Crooks <agc@pkgsrc.org>
+uid Alistair Crooks <agc@netbsd.org>
+uid Alistair Crooks <agc@netflix.com>
+
diff --git a/usr.bin/netpgpverify/data/expected23 b/usr.bin/netpgpverify/data/expected23
new file mode 100644
index 000000000000..096662a89218
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected23
@@ -0,0 +1,7 @@
+Good signature for [stdin] made Wed Sep 19 07:53:18 2012
+signature 4096/RSA (Encrypt or Sign) 064973ac4c4a706e 2009-06-23
+fingerprint: ddee 2bdb 9c98 a0d1 d4fb dbf7 0649 73ac 4c4a 706e
+uid NetBSD Security Officer <security-officer@NetBSD.org>
+encryption 4096/RSA (Encrypt or Sign) 9ff2c24fdf2ce620 2009-06-23 [Expiry 2019-06-21]
+fingerprint: 1915 0801 fbd8 f45d 89f2 0205 9ff2 c24f df2c e620
+
diff --git a/usr.bin/netpgpverify/data/expected24 b/usr.bin/netpgpverify/data/expected24
new file mode 100644
index 000000000000..1e9f824d22c7
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected24
@@ -0,0 +1,8 @@
+Good signature for [stdin] made Mon Sep 10 00:15:38 2012
+signature 2048/RSA (Encrypt or Sign) 1b68dcfcc0596823 2004-01-12
+fingerprint d415 9deb 336d e4cc cdfa 00cd 1b68 dcfc c059 6823
+uid Alistair Crooks <agc@alistaircrooks.com>
+uid Alistair Crooks <agc@pkgsrc.org>
+uid Alistair Crooks <agc@netbsd.org>
+uid Alistair Crooks <agc@netflix.com>
+
diff --git a/usr.bin/netpgpverify/data/expected25 b/usr.bin/netpgpverify/data/expected25
new file mode 100644
index 000000000000..093d9cf4a0d5
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected25
@@ -0,0 +1,7 @@
+Good signature for NetBSD-6.0_RC1_hashes.gpg made Tue Oct 16 08:12:16 2012
+signature 4096/RSA (Encrypt or Sign) 064973ac4c4a706e 2009-06-23
+fingerprint: ddee 2bdb 9c98 a0d1 d4fb dbf7 0649 73ac 4c4a 706e
+uid NetBSD Security Officer <security-officer@NetBSD.org>
+encryption 4096/RSA (Encrypt or Sign) 9ff2c24fdf2ce620 2009-06-23 [Expiry 2019-06-21]
+fingerprint: 1915 0801 fbd8 f45d 89f2 0205 9ff2 c24f df2c e620
+
diff --git a/usr.bin/netpgpverify/data/expected26 b/usr.bin/netpgpverify/data/expected26
new file mode 100644
index 000000000000..a97b231f1c2b
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected26
@@ -0,0 +1,7 @@
+Good signature for [stdin] made Tue Oct 16 08:12:16 2012
+signature 4096/RSA (Encrypt or Sign) 064973ac4c4a706e 2009-06-23
+fingerprint: ddee 2bdb 9c98 a0d1 d4fb dbf7 0649 73ac 4c4a 706e
+uid NetBSD Security Officer <security-officer@NetBSD.org>
+encryption 4096/RSA (Encrypt or Sign) 9ff2c24fdf2ce620 2009-06-23 [Expiry 2019-06-21]
+fingerprint: 1915 0801 fbd8 f45d 89f2 0205 9ff2 c24f df2c e620
+
diff --git a/usr.bin/netpgpverify/data/expected27 b/usr.bin/netpgpverify/data/expected27
new file mode 100644
index 000000000000..aeee21f0bda9
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected27
@@ -0,0 +1,5 @@
+Good signature for [stdin] made Mon Oct 15 09:28:54 2012
+signature 4096/RSA (Encrypt or Sign) 064973ac4c4a706e 2009-06-23
+fingerprint ddee 2bdb 9c98 a0d1 d4fb dbf7 0649 73ac 4c4a 706e
+uid NetBSD Security Officer <security-officer@NetBSD.org>
+
diff --git a/usr.bin/netpgpverify/data/expected28 b/usr.bin/netpgpverify/data/expected28
new file mode 100644
index 000000000000..e67338d8fe2e
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected28
@@ -0,0 +1,5 @@
+Good signature for NetBSD-6.0_hashes.asc made Mon Oct 15 09:28:54 2012
+signature 4096/RSA (Encrypt or Sign) 064973ac4c4a706e 2009-06-23
+fingerprint ddee 2bdb 9c98 a0d1 d4fb dbf7 0649 73ac 4c4a 706e
+uid NetBSD Security Officer <security-officer@NetBSD.org>
+
diff --git a/usr.bin/netpgpverify/data/expected29 b/usr.bin/netpgpverify/data/expected29
new file mode 100644
index 000000000000..8eeb1d6dc8dd
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected29
@@ -0,0 +1,7 @@
+Good signature for NetBSD-6.0_RC1_hashes_ascii.gpg made Sun Sep 9 17:41:24 2012
+signature 4096/RSA (Encrypt or Sign) 064973ac4c4a706e 2009-06-23
+fingerprint: ddee 2bdb 9c98 a0d1 d4fb dbf7 0649 73ac 4c4a 706e
+uid NetBSD Security Officer <security-officer@NetBSD.org>
+encryption 4096/RSA (Encrypt or Sign) 9ff2c24fdf2ce620 2009-06-23 [Expiry 2019-06-21]
+fingerprint: 1915 0801 fbd8 f45d 89f2 0205 9ff2 c24f df2c e620
+
diff --git a/usr.bin/netpgpverify/data/expected30 b/usr.bin/netpgpverify/data/expected30
new file mode 100644
index 000000000000..d1e6d6df6d68
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected30
@@ -0,0 +1,7 @@
+Good signature for [stdin] made Sun Sep 9 17:41:24 2012
+signature 4096/RSA (Encrypt or Sign) 064973ac4c4a706e 2009-06-23
+fingerprint: ddee 2bdb 9c98 a0d1 d4fb dbf7 0649 73ac 4c4a 706e
+uid NetBSD Security Officer <security-officer@NetBSD.org>
+encryption 4096/RSA (Encrypt or Sign) 9ff2c24fdf2ce620 2009-06-23 [Expiry 2019-06-21]
+fingerprint: 1915 0801 fbd8 f45d 89f2 0205 9ff2 c24f df2c e620
+
diff --git a/usr.bin/netpgpverify/data/expected31 b/usr.bin/netpgpverify/data/expected31
new file mode 100644
index 000000000000..1d30ff583935
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected31
@@ -0,0 +1,33 @@
+PROG=p
+SRCS=parse.c
+WARNS=5
+MKMAN=no
+CPPFLAGS+=-g -O0
+LDFLAGS+=-g -O0
+
+.include <bsd.prog.mk>
+
+t: ${PROG}
+ ./${PROG} gpgsigned-a.gpg
+PROG=p
+SRCS=parse.c
+WARNS=5
+MKMAN=no
+CPPFLAGS+=-g -O0
+LDFLAGS+=-g -O0
+
+.include <bsd.prog.mk>
+
+t: ${PROG}
+ ./${PROG} gpgsigned-a.gpg
+PROG=p
+SRCS=parse.c
+WARNS=5
+MKMAN=no
+CPPFLAGS+=-g -O0
+LDFLAGS+=-g -O0
+
+.include <bsd.prog.mk>
+
+t: ${PROG}
+ ./${PROG} gpgsigned-a.gpg
diff --git a/usr.bin/netpgpverify/data/expected32 b/usr.bin/netpgpverify/data/expected32
new file mode 100644
index 000000000000..dadff9f794ca
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected32
@@ -0,0 +1,24 @@
+Good signature for b.gpg made Mon Sep 10 00:15:38 2012
+signature 2048/RSA (Encrypt or Sign) 1b68dcfcc0596823 2004-01-12
+fingerprint d415 9deb 336d e4cc cdfa 00cd 1b68 dcfc c059 6823
+uid Alistair Crooks <agc@alistaircrooks.com>
+uid Alistair Crooks <agc@pkgsrc.org>
+uid Alistair Crooks <agc@netbsd.org>
+uid Alistair Crooks <agc@netflix.com>
+
+Good signature for b.gpg made Mon Sep 10 00:15:38 2012
+signature 2048/RSA (Encrypt or Sign) 1b68dcfcc0596823 2004-01-12
+fingerprint d415 9deb 336d e4cc cdfa 00cd 1b68 dcfc c059 6823
+uid Alistair Crooks <agc@alistaircrooks.com>
+uid Alistair Crooks <agc@pkgsrc.org>
+uid Alistair Crooks <agc@netbsd.org>
+uid Alistair Crooks <agc@netflix.com>
+
+Good signature for b.gpg made Mon Sep 10 00:15:38 2012
+signature 2048/RSA (Encrypt or Sign) 1b68dcfcc0596823 2004-01-12
+fingerprint d415 9deb 336d e4cc cdfa 00cd 1b68 dcfc c059 6823
+uid Alistair Crooks <agc@alistaircrooks.com>
+uid Alistair Crooks <agc@pkgsrc.org>
+uid Alistair Crooks <agc@netbsd.org>
+uid Alistair Crooks <agc@netflix.com>
+
diff --git a/usr.bin/netpgpverify/data/expected33 b/usr.bin/netpgpverify/data/expected33
new file mode 100644
index 000000000000..1e078bb06e73
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected33
@@ -0,0 +1,40 @@
+PROG=p
+SRCS=parse.c
+WARNS=5
+MKMAN=no
+CPPFLAGS+=-g -O0
+LDFLAGS+=-g -O0
+
+.include <bsd.prog.mk>
+
+t: ${PROG}
+ ./${PROG} gpgsigned-a.gpg
+1. tag & 0x3f
+2. len
+
+one pass (tag 4)
+========
+b version:3
+b sig type
+b hash alg
+b pubkey alg
+8b keyid
+
+literal data (tag 11)
+=============
+b binary/text
+b length
+c string
+L mtime
+text
+PROG=p
+SRCS=parse.c
+WARNS=5
+MKMAN=no
+CPPFLAGS+=-g -O0
+LDFLAGS+=-g -O0
+
+.include <bsd.prog.mk>
+
+t: ${PROG}
+ ./${PROG} gpgsigned-a.gpg
diff --git a/usr.bin/netpgpverify/data/expected34 b/usr.bin/netpgpverify/data/expected34
new file mode 100644
index 000000000000..27a6592c3122
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected34
@@ -0,0 +1,8 @@
+Good signature for det.sig made Thu Oct 18 02:12:33 2012
+signature 2048/RSA (Encrypt or Sign) 1b68dcfcc0596823 2004-01-12
+fingerprint d415 9deb 336d e4cc cdfa 00cd 1b68 dcfc c059 6823
+uid Alistair Crooks <agc@alistaircrooks.com>
+uid Alistair Crooks <agc@pkgsrc.org>
+uid Alistair Crooks <agc@netbsd.org>
+uid Alistair Crooks <agc@netflix.com>
+
diff --git a/usr.bin/netpgpverify/data/expected35 b/usr.bin/netpgpverify/data/expected35
new file mode 100644
index 000000000000..04dc803533a2
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected35
@@ -0,0 +1,16 @@
+To Do
+=====
+tests with -k sig
+detached sigs
+DSA
+
+Done
+====
+basics
+localise pgp_read_packets
+fix lint
+WARNS=5
+lib man page
+prog man page
+do we do it statically linked as well?
+multiple files in netpgpverify
diff --git a/usr.bin/netpgpverify/data/expected36 b/usr.bin/netpgpverify/data/expected36
new file mode 100644
index 000000000000..6ea2a3d30d68
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected36
@@ -0,0 +1,5 @@
+Good signature for in1.gpg made Mon Oct 22 04:45:41 2012
+signature 2048/DSA 263fe78562e2fc7e 2012-10-21
+fingerprint d2e5 07b6 5d59 33d3 9c8d a618 263f e785 62e2 fc7e
+uid David Armstrong (Test DSA key - do not use) <dsa@dsa.com>
+
diff --git a/usr.bin/netpgpverify/data/expected37 b/usr.bin/netpgpverify/data/expected37
new file mode 100644
index 000000000000..1e6162a68e35
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected37
@@ -0,0 +1,5 @@
+Good signature for [stdin] made Mon Oct 22 04:45:41 2012
+signature 2048/DSA 263fe78562e2fc7e 2012-10-21
+fingerprint d2e5 07b6 5d59 33d3 9c8d a618 263f e785 62e2 fc7e
+uid David Armstrong (Test DSA key - do not use) <dsa@dsa.com>
+
diff --git a/usr.bin/netpgpverify/data/expected38 b/usr.bin/netpgpverify/data/expected38
new file mode 100644
index 000000000000..65f8b1fc90df
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected38
@@ -0,0 +1,5 @@
+Good signature for in1.asc made Mon Oct 22 04:45:26 2012
+signature 2048/DSA 263fe78562e2fc7e 2012-10-21
+fingerprint d2e5 07b6 5d59 33d3 9c8d a618 263f e785 62e2 fc7e
+uid David Armstrong (Test DSA key - do not use) <dsa@dsa.com>
+
diff --git a/usr.bin/netpgpverify/data/expected39 b/usr.bin/netpgpverify/data/expected39
new file mode 100644
index 000000000000..b6d57029d3cf
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected39
@@ -0,0 +1,5 @@
+Good signature for [stdin] made Mon Oct 22 04:45:26 2012
+signature 2048/DSA 263fe78562e2fc7e 2012-10-21
+fingerprint d2e5 07b6 5d59 33d3 9c8d a618 263f e785 62e2 fc7e
+uid David Armstrong (Test DSA key - do not use) <dsa@dsa.com>
+
diff --git a/usr.bin/netpgpverify/data/expected40 b/usr.bin/netpgpverify/data/expected40
new file mode 100644
index 000000000000..a4bda3c562f9
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected40
@@ -0,0 +1,6 @@
+# .NetBSD: Makefile,v 1.5.10.1 2012/05/06 18:14:16 agc Exp .
+
+SUBDIR+= lib .WAIT
+SUBDIR+= bin
+
+.include <bsd.subdir.mk>
diff --git a/usr.bin/netpgpverify/data/expected41 b/usr.bin/netpgpverify/data/expected41
new file mode 100644
index 000000000000..a4bda3c562f9
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected41
@@ -0,0 +1,6 @@
+# .NetBSD: Makefile,v 1.5.10.1 2012/05/06 18:14:16 agc Exp .
+
+SUBDIR+= lib .WAIT
+SUBDIR+= bin
+
+.include <bsd.subdir.mk>
diff --git a/usr.bin/netpgpverify/data/expected42 b/usr.bin/netpgpverify/data/expected42
new file mode 100644
index 000000000000..a4bda3c562f9
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected42
@@ -0,0 +1,6 @@
+# .NetBSD: Makefile,v 1.5.10.1 2012/05/06 18:14:16 agc Exp .
+
+SUBDIR+= lib .WAIT
+SUBDIR+= bin
+
+.include <bsd.subdir.mk>
diff --git a/usr.bin/netpgpverify/data/expected43 b/usr.bin/netpgpverify/data/expected43
new file mode 100644
index 000000000000..a4bda3c562f9
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected43
@@ -0,0 +1,6 @@
+# .NetBSD: Makefile,v 1.5.10.1 2012/05/06 18:14:16 agc Exp .
+
+SUBDIR+= lib .WAIT
+SUBDIR+= bin
+
+.include <bsd.subdir.mk>
diff --git a/usr.bin/netpgpverify/data/expected44 b/usr.bin/netpgpverify/data/expected44
new file mode 100644
index 000000000000..a8786ee113c9
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected44
@@ -0,0 +1,5 @@
+Good signature for in2.gpg made Mon Oct 22 06:24:09 2012
+signature 2048/DSA 263fe78562e2fc7e 2012-10-21
+fingerprint d2e5 07b6 5d59 33d3 9c8d a618 263f e785 62e2 fc7e
+uid David Armstrong (Test DSA key - do not use) <dsa@dsa.com>
+
diff --git a/usr.bin/netpgpverify/data/expected45 b/usr.bin/netpgpverify/data/expected45
new file mode 100644
index 000000000000..379ffce293e8
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected45
@@ -0,0 +1,5 @@
+Good signature for in2.asc made Mon Oct 22 06:24:26 2012
+signature 2048/DSA 263fe78562e2fc7e 2012-10-21
+fingerprint d2e5 07b6 5d59 33d3 9c8d a618 263f e785 62e2 fc7e
+uid David Armstrong (Test DSA key - do not use) <dsa@dsa.com>
+
diff --git a/usr.bin/netpgpverify/data/expected46 b/usr.bin/netpgpverify/data/expected46
new file mode 100644
index 000000000000..4c262d05b98e
--- /dev/null
+++ b/usr.bin/netpgpverify/data/expected46
@@ -0,0 +1,8 @@
+Ignoring unusual/reserved signature subpacket 18
+Good signature for NetBSD-6.0_hashes.asc made Mon Oct 15 09:28:54 2012
+signature 4096/RSA (Encrypt or Sign) 064973ac4c4a706e 2009-06-23
+fingerprint: ddee 2bdb 9c98 a0d1 d4fb dbf7 0649 73ac 4c4a 706e
+uid NetBSD Security Officer <security-officer@NetBSD.org>
+encryption 4096/RSA (Encrypt or Sign) 9ff2c24fdf2ce620 2009-06-23 [Expiry 2019-06-21]
+fingerprint: 1915 0801 fbd8 f45d 89f2 0205 9ff2 c24f df2c e620
+
diff --git a/usr.bin/netpgpverify/data/in1.asc b/usr.bin/netpgpverify/data/in1.asc
new file mode 100644
index 000000000000..d2b79f60f77d
--- /dev/null
+++ b/usr.bin/netpgpverify/data/in1.asc
@@ -0,0 +1,16 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA256
+
+# .NetBSD: Makefile,v 1.5.10.1 2012/05/06 18:14:16 agc Exp .
+
+SUBDIR+= lib .WAIT
+SUBDIR+= bin
+
+.include <bsd.subdir.mk>
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.11 (NetBSD)
+
+iF4EAREIAAYFAlCFMdYACgkQJj/nhWLi/H7CkQEAgDQrFwPD76JC+6FnOKEz/9DP
+H7WjRRMoIQNTGC3ZXRsA/1xah8eFePQDmTO1sQGnINbgX9vZ7GAFOgTjW7+tVb7H
+=wtKb
+-----END PGP SIGNATURE-----
diff --git a/usr.bin/netpgpverify/data/in1.gpg b/usr.bin/netpgpverify/data/in1.gpg
new file mode 100644
index 000000000000..3e96e861ff56
--- /dev/null
+++ b/usr.bin/netpgpverify/data/in1.gpg
Binary files differ
diff --git a/usr.bin/netpgpverify/data/in2.asc b/usr.bin/netpgpverify/data/in2.asc
new file mode 100644
index 000000000000..aaf8e119456c
--- /dev/null
+++ b/usr.bin/netpgpverify/data/in2.asc
@@ -0,0 +1,16 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA256
+
+# .NetBSD: Makefile,v 1.5.10.1 2012/05/06 18:14:16 agc Exp .
+
+SUBDIR+= lib .WAIT
+SUBDIR+= bin
+
+.include <bsd.subdir.mk>
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.11 (NetBSD)
+
+iF4EAREIAAYFAlCFSQoACgkQJj/nhWLi/H6aKAD9HCLTwY8CwiqTXrzKxHZ5lHQn
+qEZbcbXjkCxlk+m/PHUA/2Whlc0t5ZtmI221LQy5inTnzpu1U75E5lJvw0YMTdXJ
+=v788
+-----END PGP SIGNATURE-----
diff --git a/usr.bin/netpgpverify/data/in2.gpg b/usr.bin/netpgpverify/data/in2.gpg
new file mode 100644
index 000000000000..83a7dfbe61fc
--- /dev/null
+++ b/usr.bin/netpgpverify/data/in2.gpg
Binary files differ
diff --git a/usr.bin/netpgpverify/data/jj.asc b/usr.bin/netpgpverify/data/jj.asc
new file mode 100644
index 000000000000..65661d69f8db
--- /dev/null
+++ b/usr.bin/netpgpverify/data/jj.asc
@@ -0,0 +1,32 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+1. tag & 0x3f
+2. len
+
+one pass (tag 4)
+========
+b version:3
+b sig type
+b hash alg
+b pubkey alg
+8b keyid
+
+literal data (tag 11)
+=============
+b binary/text
+b length
+c string
+L mtime
+text
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.11 (NetBSD)
+
+iQEcBAEBAgAGBQJQaIZcAAoJEBto3PzAWWgj678IALbDHon3Rm6qUhn7k1TFT6D3
+yi/jzf3piSJGsgUg2wEghs175edC/cJK3lG9Gx/3/uQq06R9g37nVRX8I0sK7yT2
+XgR+RHoGh/b+CQxdRNC+ub5QoNb8LcmCb/MQGq2KK9otSExiy4WMUP4K1DblaK5L
++Hg4VTooMot1NVqyFSoB2aZauXc2F4ZVh5q0fn8w5GEw45P+AUUbmzpgbLwXbl+I
+tMsX54V1dxyDcCYUs0xUH/VxJUQEeIlDbCOXYMbCVtggYRqKksTr+u/riw/Llnql
+jQdq5rBRW1SlD7Ll6z/LF2WBJOWtHzp4qbnBGSq5uB1q37H3mWL28f1tL//TUjM=
+=EX8W
+-----END PGP SIGNATURE-----
diff --git a/usr.bin/netpgpverify/data/message b/usr.bin/netpgpverify/data/message
new file mode 100644
index 000000000000..08844f75c649
--- /dev/null
+++ b/usr.bin/netpgpverify/data/message
@@ -0,0 +1 @@
+This is an example messsage.
diff --git a/usr.bin/netpgpverify/data/message.keyring b/usr.bin/netpgpverify/data/message.keyring
new file mode 100644
index 000000000000..2ae71b9e2d7f
--- /dev/null
+++ b/usr.bin/netpgpverify/data/message.keyring
Binary files differ
diff --git a/usr.bin/netpgpverify/data/message.v1.asc b/usr.bin/netpgpverify/data/message.v1.asc
new file mode 100644
index 000000000000..238ee4df345b
--- /dev/null
+++ b/usr.bin/netpgpverify/data/message.v1.asc
@@ -0,0 +1,14 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA1
+
+This is an example messsage.
+-----BEGIN PGP SIGNATURE-----
+
+iQEcBAEBAgAGBQJpe96fAAoJEIGRNTbVsiooKJAIAI7DdO0v1pqIF/EdCELBLmSk
+BlKFeKIl2k5GEh6Z6LUHebiJd1GfpNN7Pf99oqyRm9dg3bDdfZ6BdJkiEJtEQ8kz
+PXqoMhdSQ9rtmYxyZBMYVcGFFizl/mpXupwi9Rq7hRogr7NDLaJVFwwDrGrx5mSU
+A2pKAeejB0F5l/rbpfzErqnajBeCZmzjCkPlgfKGMSSiVONC2rxTYyJUa6pSLruq
+jrfgfsmDk6TLKXA8xNchCyMJoIs3Drtq7XeY9Y7FFPEY9hFH8+YYZmElaK8XOzYy
+tLeMCwCdLMwL7mmS2rFAg4U60zIYUzWI1nWBaHBVlv50mtTuiNN83/axIpoI3dE=
+=cW1k
+-----END PGP SIGNATURE-----
diff --git a/usr.bin/netpgpverify/data/message.v1.asc.expected b/usr.bin/netpgpverify/data/message.v1.asc.expected
new file mode 100644
index 000000000000..14e76b1ced74
--- /dev/null
+++ b/usr.bin/netpgpverify/data/message.v1.asc.expected
@@ -0,0 +1,5 @@
+Good signature for message.v1.asc made Thu Jan 29 23:26:39 2026
+signature 2048/RSA (Encrypt or Sign) 81913536d5b22a28 2026-01-29
+fingerprint 5bc4 b091 22be 606e d732 8a08 8191 3536 d5b2 2a28
+uid Test Key <test@example.com>
+
diff --git a/usr.bin/netpgpverify/data/message.v1.sig b/usr.bin/netpgpverify/data/message.v1.sig
new file mode 100644
index 000000000000..c2bc90f3d971
--- /dev/null
+++ b/usr.bin/netpgpverify/data/message.v1.sig
Binary files differ
diff --git a/usr.bin/netpgpverify/data/message.v1.sig.expected b/usr.bin/netpgpverify/data/message.v1.sig.expected
new file mode 100644
index 000000000000..362d5f2aaa31
--- /dev/null
+++ b/usr.bin/netpgpverify/data/message.v1.sig.expected
@@ -0,0 +1,5 @@
+Good signature for message.v1.sig made Thu Jan 29 23:26:23 2026
+signature 2048/RSA (Encrypt or Sign) 81913536d5b22a28 2026-01-29
+fingerprint 5bc4 b091 22be 606e d732 8a08 8191 3536 d5b2 2a28
+uid Test Key <test@example.com>
+
diff --git a/usr.bin/netpgpverify/data/message.v2.asc b/usr.bin/netpgpverify/data/message.v2.asc
new file mode 100644
index 000000000000..abff22db4e15
--- /dev/null
+++ b/usr.bin/netpgpverify/data/message.v2.asc
@@ -0,0 +1,15 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA256
+
+This is an example messsage.
+-----BEGIN PGP SIGNATURE-----
+
+iQEzBAEBCAAdFiEEW8SwkSK+YG7XMooIgZE1NtWyKigFAml730EACgkQgZE1NtWy
+KijBCwf/aMxfreT1hNjzwfE9cHFVgjfI+P0pzuf4HVM6Xv3niQAV6Kn8hIn4hlS2
+oyo1aKoqek8EKKBHXWi6oTQ3AjfsJDzte2axE2w1TKCRQucSL7kaJAFpHmTM0gr6
+nQsoba1aFuxIa9ENRs5SsW47IdR5gFTg9QXdX2LepGFHDk9kULOy90xt2aHXpdft
+COvz/9KTok97gi+Tc0YsF+IExIQe0/LkadziKVI3GwyEgksaJw6h7aCF5YxA6N5c
+R6jgGmBtKzjeDb8IbFH+IRuBZGzt2r7a3CiScgOjm1T3Z/kVETBor7nBYCcb5S6q
+dIZlIMOdempGnvznuzBJGZBWLECMxQ==
+=5+xC
+-----END PGP SIGNATURE-----
diff --git a/usr.bin/netpgpverify/data/message.v2.asc.expected b/usr.bin/netpgpverify/data/message.v2.asc.expected
new file mode 100644
index 000000000000..358f71d49ae9
--- /dev/null
+++ b/usr.bin/netpgpverify/data/message.v2.asc.expected
@@ -0,0 +1,5 @@
+Good signature for message.v2.asc made Thu Jan 29 23:29:21 2026
+signature 2048/RSA (Encrypt or Sign) 81913536d5b22a28 2026-01-29
+fingerprint 5bc4 b091 22be 606e d732 8a08 8191 3536 d5b2 2a28
+uid Test Key <test@example.com>
+
diff --git a/usr.bin/netpgpverify/data/message.v2.sig b/usr.bin/netpgpverify/data/message.v2.sig
new file mode 100644
index 000000000000..09cf3957d628
--- /dev/null
+++ b/usr.bin/netpgpverify/data/message.v2.sig
Binary files differ
diff --git a/usr.bin/netpgpverify/data/message.v2.sig.expected b/usr.bin/netpgpverify/data/message.v2.sig.expected
new file mode 100644
index 000000000000..0baf1a35684f
--- /dev/null
+++ b/usr.bin/netpgpverify/data/message.v2.sig.expected
@@ -0,0 +1,5 @@
+Good signature for message.v2.sig made Thu Jan 29 23:29:14 2026
+signature 2048/RSA (Encrypt or Sign) 81913536d5b22a28 2026-01-29
+fingerprint 5bc4 b091 22be 606e d732 8a08 8191 3536 d5b2 2a28
+uid Test Key <test@example.com>
+
diff --git a/usr.bin/netpgpverify/data/pubring.gpg b/usr.bin/netpgpverify/data/pubring.gpg
new file mode 100644
index 000000000000..796a40a1398a
--- /dev/null
+++ b/usr.bin/netpgpverify/data/pubring.gpg
Binary files differ
diff --git a/usr.bin/shmif_pcapin/Makefile b/usr.bin/shmif_pcapin/Makefile
new file mode 100644
index 000000000000..30a9f8347f72
--- /dev/null
+++ b/usr.bin/shmif_pcapin/Makefile
@@ -0,0 +1,11 @@
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/usr.bin/shmif_pcapin
+FILESDIR= ${TESTSDIR}
+
+TESTS_SH+= t_basic
+TESTS_SH_SRC_t_basic= ../../net/net_common.sh t_basic.sh
+
+FILES+= d_pcap.in.bz2.uue d_pcap.out.bz2.uue
+
+.include <bsd.test.mk>
diff --git a/usr.bin/shmif_pcapin/d_pcap.in.bz2.uue b/usr.bin/shmif_pcapin/d_pcap.in.bz2.uue
new file mode 100644
index 000000000000..f7bebd31beb4
--- /dev/null
+++ b/usr.bin/shmif_pcapin/d_pcap.in.bz2.uue
@@ -0,0 +1,27 @@
+begin 644 d_pcap.in.bz2
+M0EIH.3%!6293671X8T@`",M_______TOA>KK]E_____W_____^\C:_:XZKOZ
+M]#3:(5__T`/^`.@`&YM"%4,@-`!DQJ````T9&@``````T-!DT`&@````````
+M`:`/*%*@]1^GJHT`!ZAM(```#0T```:`!H:``&@````````````#1"?_ZJJ8
+MF`F```````!,``````````$R8````````$PFF(`T#30:#$R`&AD&C`(`#31D
+M!A`9,@R:9-#$9,@-``:,@8F3(```#0`"HI(HU-I)Z0]0]1Z@`&F@:``-H@TR
+M#:0\I_ZJ@]1I@@/4:,1ZAM33:FAIM3)ZCU-&F)^IFE/4\D:-E!Z@T:-J;-SK
+MR`JJ%4J5)T*)-32B3R*)+)(CJ$2:E1)B(DZE826U/9(DPB3#@JQ9*B2PSSSS
+M9LVP9M5FS9LV;-FS9LV;-FS9LV;-FS9YY^B\\0\^J17N5=!4RI/5U,(DK%&*
+M+4Q6)$F+99227:0M%1)4F*+2]"26DF2),B\_$F%B2IHRPQ<&+5MEM7SBR),+
+M5NA$EP56V8PDD<U$E)I5'IE.XK)2KZVOS,FEDU^3H9-##$KC#IC=6T``````
+M`````````$!`^\_VJ&P;!ZKK/X\O9-8[E>O%K^>4,BKE+2UEE#G&E557XWT6
+M3)?&:'#=Z_8XKD/#<ISSQ7-=VYYWS6>=<=X3R;:N`W+<OE/FN&[MK/DNM:K5
+M5:B37(DK%Y:L63LU<^WK#KEAW"S0K;L&)$F_]WVR)-HB31I%Y*Z_'P[K:\!$
+MF6Q6N(B3'5:O3LMHB39(DP23<+QU<XI5B#=[S`R4V:)+.3X6#M>!@X2)+/;J
+MU(2I$E@62254G\/$Y<AH=2T]`^6^\W3K42:>P?68W+;-5NVC?]BKLE>D1)[!
+MAIV+L63:-'9,F.S=SP=#1O&'LU;Y7VE;5]MAL?N,,>Q8=$KE-&_9/NJR[5AT
+M:OGJ<Q6[5-A3?.Z='X&@\TYB1NVX=XZIJ.*X;P'>O>NL=>UG?N*_6UW1/!<1
+MQ&V<1X+7<5Q7Q&NZMWW?]LUG\G&>&^DU72O+N4YCO'':6\=HWSKF]=H_JUVT
+M8;IXC8."T.1QMN^1^'IG^6R;C>OZ$2<)>2[!I=6\JB2(!0'LHS1`1*+YBP:G
+M<^L*HT2-$4:2-E635)24E@E*SFA]Z1`1.%C!!DXL&*2VN*BL9^L*\M)6`ZE7
+M35\PZ>-(A%.BD$,U)2TG'S,O.%EED(0A"(HOTT<OU]O?XC?D```````HI]&B
+MI4911111B@:(7\)-.;XQ#&>&(8]>)+$G;[YCYC+M_H;?9NHB5R&HPYOI]5HZ
+M>OIO&8((JP?0=@*@/^`GMR#G\",%>3">$0!3L@_2.$DN8QPFL,L#<G,@M@,:
+J-JKQW2,:6F)XLZ5PWCHDXS0;&N$ITBCG5:$2?OYN3_XNY(IPH2#H\,:0
+`
+end
diff --git a/usr.bin/shmif_pcapin/d_pcap.out.bz2.uue b/usr.bin/shmif_pcapin/d_pcap.out.bz2.uue
new file mode 100644
index 000000000000..616bf2369559
--- /dev/null
+++ b/usr.bin/shmif_pcapin/d_pcap.out.bz2.uue
@@ -0,0 +1,13 @@
+begin 644 d_pcap.out.bz2
+M0EIH.3%!62936>CJBS$`#$]?@``00&5_\0@B0``_Y?]@0`*8VP``<_2/4T_U
+M4!,``0P1AS_]50(9#31D9&1H9,@)JDDTF*>13U/4&0>IFB:;:H8`9--!D,$-
+M,1HQTI'UI>:/S1B,1J3^)&3UD9,D=9'Y(_1'A(_J1RD?*1Y5(]I&2/&1SD<D
+M<T??OF9F````````".THE5<[;+;H6@P`!555408@``-!@!SY[[[[[[\_C(\)
+M'"D<Y&J1D)=NFVM:^5(Q&D>$C$:1WD<9'K2/YD<N/'-:RY2,KQD95JD9(=9&
+M^M]9K4C:D:N$C*OWD962,K_*1OQVUK761DC>1VD=Y''COK6MY'G(VD<,X:UK
+MVD;R.'#AK6M4C)&I&LUK6MI&U(R1FVV:UY2.TCTD>,C4C?>S)'61QD;2,D=)
+M'I(X2/A(X2.E([2.5(^<C*1\Y'TD;2/*D?1&Z,1B/61]I'_2-I'WI'X1[R,1
+M]I'=&\CI(])'>D>T)>\C>$O@CM2.<CZR-4CA(R1DC$=4=Z1J1VD>Z/Q(^,CS
+.1VD?^+N2*<*$AT=468@C
+`
+end
diff --git a/usr.bin/shmif_pcapin/t_basic.sh b/usr.bin/shmif_pcapin/t_basic.sh
new file mode 100644
index 000000000000..ef364878e4bd
--- /dev/null
+++ b/usr.bin/shmif_pcapin/t_basic.sh
@@ -0,0 +1,89 @@
+# $NetBSD: t_basic.sh,v 1.1 2024/09/02 05:16:37 ozaki-r Exp $
+#
+# Copyright (c) 2017-2018 Internet Initiative Japan Inc.
+# Copyright (c) 2011 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+SOCK=unix://commsock
+BUS=bus0
+
+unpack_file()
+{
+
+ atf_check -s exit:0 uudecode $(atf_get_srcdir)/${1}.bz2.uue
+ atf_check -s exit:0 bunzip2 -f ${1}.bz2
+}
+
+atf_test_case pcap cleanup
+
+pcap_head()
+{
+
+ atf_set "descr" "Write frames from pcap(3) file to shmif(4) interface"
+}
+
+pcap_body()
+{
+ unpack_file d_pcap.in
+ unpack_file d_pcap.out
+
+ rump_server_npf_start ${SOCK} # need librumpdev_bpf
+ rump_server_add_iface ${SOCK} shmif0 ${BUS}
+
+ export RUMP_SERVER=${SOCK}
+ export LD_PRELOAD=/usr/lib/librumphijack.so
+ export RUMPHIJACK=path=/rump,socket=all:nolocal,sysctl=yes,,blanket=/dev/bpf
+
+ atf_check -s exit:0 rump.ifconfig shmif0 up
+
+ # Capture frames on shmif0 to examine later.
+ tcpdump -c 58 -eni shmif0 -w shmif0.in.pcap &
+ sleep 1 # give shmif0 a change to turn into promiscuous mode
+ # Write frames to the bus.
+ atf_check -s exit:0 -o ignore shmif_pcapin d_pcap.in ${BUS}
+ wait # for tcpdump to exit
+
+ # Check if written frames surely arrives at shmif0.
+ atf_check -s exit:0 -o match:"input: 58 packets, 5684 bytes" rump.ifconfig -v shmif0
+ # Check if frames captured on shmif0 are expected ones.
+ atf_check -s exit:0 -o file:d_pcap.out -e ignore tcpdump -entr shmif0.in.pcap
+
+ unset LD_PRELOAD
+ unset RUMP_SERVER
+}
+
+pcap_cleanup()
+{
+
+ $DEBUG && dump
+ cleanup
+}
+
+
+atf_init_test_cases()
+{
+
+ atf_add_test_case pcap
+}
diff --git a/usr.bin/stat/Makefile b/usr.bin/stat/Makefile
new file mode 100644
index 000000000000..9118b2434b8f
--- /dev/null
+++ b/usr.bin/stat/Makefile
@@ -0,0 +1,7 @@
+# $NetBSD: Makefile,v 1.1 2024/03/14 21:00:33 rillig Exp $
+
+TESTSDIR= ${TESTSBASE}/usr.bin/stat
+
+TESTS_SH= t_stat
+
+.include <bsd.test.mk>
diff --git a/usr.bin/stat/t_stat.sh b/usr.bin/stat/t_stat.sh
new file mode 100644
index 000000000000..b4bc56320d18
--- /dev/null
+++ b/usr.bin/stat/t_stat.sh
@@ -0,0 +1,68 @@
+# $NetBSD: t_stat.sh,v 1.2 2024/03/14 21:17:54 rillig Exp $
+#
+# Copyright (c) 2024 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# This code is derived from software contributed to The NetBSD Foundation
+# by Roland Illig.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+atf_test_case string_format
+string_format_head() {
+ atf_set "descr" "Tests the string output format 'S'"
+}
+string_format_body() {
+ echo 'äöü' > './Ümläute'
+
+ atf_check -o 'inline:plain <Ümläute>\n' \
+ stat -f 'plain <%SN>' 'Ümläute'
+
+ atf_check -o 'inline:right-aligned < Ümläute>\n' \
+ stat -f 'right-aligned <%20SN>' 'Ümläute'
+
+ atf_check -o 'inline:left-aligned <Ümläute >\n' \
+ stat -f 'left-aligned <%-20SN>' 'Ümläute'
+
+ atf_check -s exit:1 -o ignore -e 'inline:stat: % SN: bad format\n' \
+ stat -f 'string-space <% SN>' 'Ümläute'
+
+ atf_check -s exit:1 -o ignore -e 'inline:stat: %+SN: bad format\n' \
+ stat -f 'string-plus <%+SN>' 'Ümläute'
+
+ atf_check -s exit:1 -o ignore -e 'inline:stat: %0SN: bad format\n' \
+ stat -f 'string-zero <%0SN>' 'Ümläute'
+
+ atf_check -o 'inline:vis <\303\234ml\303\244ute>\n' \
+ stat -f 'vis <%#SN>' 'Ümläute'
+
+ atf_check -o 'inline:vis left-aligned <\303\234ml\303\244ute >\n' \
+ stat -f 'vis left-aligned <%#-30SN>' 'Ümläute'
+
+ atf_check -o 'inline:vis right-aligned < \303\234ml\303\244ute>\n' \
+ stat -f 'vis right-aligned <%#30SN>' 'Ümläute'
+}
+
+atf_init_test_cases() {
+ atf_add_test_case string_format
+}
diff --git a/usr.bin/xlint/lint1/expr.c b/usr.bin/xlint/lint1/expr.c
new file mode 100644
index 000000000000..77f64ffd00ba
--- /dev/null
+++ b/usr.bin/xlint/lint1/expr.c
@@ -0,0 +1,33 @@
+/* $NetBSD: expr.c,v 1.1 2024/06/08 09:09:20 rillig Exp $ */
+# 3 "expr.c"
+
+/*
+ * Miscellaneous tests for expressions.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+struct bit_fields {
+ unsigned u32: 32;
+};
+
+unsigned long
+expr_cond_cvt(unsigned long ul)
+{
+ struct bit_fields bits = { 0 };
+ // Combining 'unsigned:32' and 'unsigned long' in the ':' operator
+ // results in 'unsigned long'.
+ return bits.u32 < ul ? bits.u32 : ul;
+}
+
+// Before tree.c 1.76 from 2014-04-17, lint crashed with an internal error,
+// due to an uninitialized right operand of a tree node.
+void
+crash_in_assignment(void)
+{
+ /* expect+1: warning: 'x' set but not used in function 'crash_in_assignment' [191] */
+ double x = 1;
+ int foo = 0;
+ if (foo)
+ x = 1;
+}
diff --git a/usr.bin/xlint/lint1/gcc.c b/usr.bin/xlint/lint1/gcc.c
new file mode 100644
index 000000000000..a239bfd83220
--- /dev/null
+++ b/usr.bin/xlint/lint1/gcc.c
@@ -0,0 +1,130 @@
+/* $NetBSD: gcc.c,v 1.7 2026/01/20 23:46:36 rillig Exp $ */
+# 3 "gcc.c"
+
+/*
+ * Miscellaneous tests that are specific to lint's GCC mode.
+ */
+
+/* lint1-extra-flags: -chaapbrz -X 351 */
+
+// Before C99 introduced __func__, GCC already had __FUNCTION__ with the same
+// semantics.
+const char *
+gcc_function(void)
+{
+ /* expect+1: error: negative array dimension (-13) [20] */
+ typedef int size[-(int)sizeof __FUNCTION__];
+
+ return __FUNCTION__;
+}
+
+// Before C99 introduced designators in initializers, GCC already had them,
+// although with a different syntax for struct/union members and with the
+// a...b for ranges of array elements.
+int array_range_initializers[256] = {
+ [2] = 1,
+ [3] = 2,
+ [4 ... 5] = 3
+};
+
+_Bool dbl_isinf(double);
+
+// Test that the GCC '__extension__' and '__typeof' are recognized.
+void
+extension_and_typeof(void)
+{
+ double __logbw = 1;
+ if (__extension__(({
+ __typeof((__logbw)) x_ = (__logbw);
+ !dbl_isinf((x_));
+ })))
+ __logbw = 1;
+}
+
+int
+range_in_case_label(int i)
+{
+ switch (i) {
+ case 1 ... 40: // This is a GCC extension.
+ return 1;
+ default:
+ return 2;
+ }
+}
+
+union {
+ int i;
+ char *s;
+} initialize_union_with_mixed_designators[] = {
+ { i: 1 }, /* GCC-style */
+ { s: "foo" }, /* GCC-style */
+ { .i = 1 }, /* C99-style */
+ { .s = "foo" } /* C99-style */
+};
+
+union {
+ int i[10];
+ short s;
+} initialize_union_with_gcc_designators[] = {
+ { s: 2 },
+ { i: { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } },
+};
+
+void
+declaration_of_variable_array(int i)
+{
+ int array[i];
+ while (i-- > 0)
+ array[i] = 0;
+}
+
+/*
+ * Before cgram.y 1.226 from 2021-05-03, lint could not parse typeof(...) if
+ * there was a statement before it.
+ */
+void *
+typeof_after_statement(void **ptr)
+{
+ return ({
+ if (*ptr != (void *)0)
+ ptr++;
+ __typeof__(*ptr) ret = *ptr;
+ ret;
+ });
+}
+
+const char *
+auto_type(const char *ptr)
+{
+ __auto_type pp = &ptr;
+ return *pp;
+}
+
+void
+atomic_functions(void)
+{
+ static unsigned long long v, *pv;
+ v = __atomic_load_n(pv, 0);
+ v = __atomic_exchange_n(pv, 0, 0);
+ v = __atomic_add_fetch(pv, 0, 0);
+ v = __atomic_sub_fetch(pv, 0, 0);
+ v = __atomic_and_fetch(pv, 0, 0);
+ v = __atomic_xor_fetch(pv, 0, 0);
+ v = __atomic_or_fetch(pv, 0, 0);
+ v = __atomic_nand_fetch(pv, 0, 0);
+ v = __atomic_fetch_add(pv, 0, 0);
+ v = __atomic_fetch_sub(pv, 0, 0);
+ v = __atomic_fetch_and(pv, 0, 0);
+ v = __atomic_fetch_xor(pv, 0, 0);
+ v = __atomic_fetch_or(pv, 0, 0);
+ v = __atomic_fetch_nand(pv, 0, 0);
+
+ static char c, *pc;
+ c = __atomic_load_n(pc, 0);
+ c = __atomic_exchange_n(pc, 0, 0);
+
+ /* expect+1: warning: conversion from 'unsigned long long' to 'char' may lose accuracy [132] */
+ c = __atomic_exchange_n(pv, 0, 0);
+}
+
+__float128 f128 = 0.0;
diff --git a/usr.bin/xlint/lint1/init_c99.c b/usr.bin/xlint/lint1/init_c99.c
new file mode 100644
index 000000000000..a9a72552bba2
--- /dev/null
+++ b/usr.bin/xlint/lint1/init_c99.c
@@ -0,0 +1,739 @@
+/* $NetBSD: init_c99.c,v 1.5 2025/04/12 15:49:50 rillig Exp $ */
+# 3 "init_c99.c"
+
+// Tests for initialization in C99 or later, mainly for designators.
+//
+// See C99 6.7.8 "Initialization".
+
+/* lint1-flags: -Sw -X 351 */
+
+void use(const void *);
+
+typedef struct any {
+ const void *value;
+} any;
+
+
+// C99 6.7.8p11 says "optionally enclosed in braces". Whether this wording
+// means "a single pair of braces" or "as many pairs of braces as you want"
+// is left for interpretation to the reader.
+int scalar_without_braces = 3;
+int scalar_with_optional_braces = { 3 };
+int scalar_with_too_many_braces = {{ 3 }};
+/* expect+1: error: too many initializers for 'int' [174] */
+int scalar_with_too_many_initializers = { 3, 5 };
+
+
+// See initialization_expr, 'handing over to INIT'.
+void
+struct_initialization_via_assignment(any arg)
+{
+ any local = arg;
+ use(&local);
+}
+
+
+// See initialization_expr, initialization_init_array_from_string.
+char static_duration[] = "static duration";
+signed char static_duration_signed[] = "static duration";
+unsigned char static_duration_unsigned[] = "static duration";
+int static_duration_wchar[] = L"static duration";
+
+// See init_expr.
+void
+initialization_by_braced_string(void)
+{
+ any local = { "hello" };
+ use(&local);
+}
+
+void
+initialization_by_redundantly_braced_string(void)
+{
+ any local = {{{{ "hello" }}}};
+ use(&local);
+}
+
+/*
+ * Only scalar expressions and string literals may be enclosed by additional
+ * braces. Since 'arg' is a struct, this is a compile-time error.
+ */
+void
+initialization_with_too_many_braces(any arg)
+{
+ /* expect+1: error: cannot initialize 'pointer to const void' from 'struct any' [185] */
+ any local = { arg };
+ use(&arg);
+}
+
+// Some of the following examples are mentioned in the introduction comment
+// in init.c.
+
+int number = 12345;
+
+int number_with_braces_and_comma = {
+ 12345,
+};
+
+int array_with_fixed_size[3] = {
+ 111,
+ 222,
+ 333,
+ /* expect+1: error: too many array initializers, expected 3 [173] */
+ 444,
+};
+
+// See update_type_of_array_of_unknown_size.
+int array_of_unknown_size[] = {
+ 111,
+ 222,
+ 333,
+};
+
+int array_flat[2][2] = {
+ 11,
+ 12,
+ 21,
+ 22
+};
+
+int array_nested[2][2] = {
+ {
+ 11,
+ 12
+ },
+ {
+ 21,
+ 22
+ }
+};
+
+int array_with_designators[] = {
+ ['1'] = 111,
+ ['5'] = 555,
+ ['9'] = 999
+};
+
+int array_with_some_designators[] = {
+ ['1'] = 111,
+ 222,
+ ['9'] = 999
+};
+
+struct point {
+ int x;
+ int y;
+};
+
+struct point point = {
+ 3,
+ 4
+};
+
+struct point point_with_designators = {
+ .y = 4,
+ .x = 3,
+};
+
+struct point point_with_mixed_designators = {
+ .x = 3,
+ 4,
+ /* expect+1: error: too many struct/union initializers [172] */
+ 5,
+ .x = 3,
+};
+
+/*
+ * Before cgram.y 1.230 from 2021-06-20, the grammar allowed either of the
+ * operators '.' or '->' to be used for the designators and had extra code
+ * to ensure that only '.' was actually used.
+ */
+struct point origin = {
+ .x = 0,
+ /* expect+1: error: syntax error '->' [249] */
+ ->y = 0,
+};
+
+/* Ensure that the parser can recover from the parse error. */
+struct point pythagoras = { 3, 4 };
+
+int array_with_designator[] = {
+ 111,
+ /* expect+1: error: syntax error 'designator '.member' is only for struct/union' [249] */
+ .member = 222,
+ 333,
+};
+
+/*
+ * C99 6.7.8p11 says that the initializer of a scalar can be "optionally
+ * enclosed in braces". It does not explicitly set an upper limit on the
+ * number of braces. It also doesn't restrict the term "initializer" to only
+ * mean the "outermost initializer". 6.7.8p13 defines that a brace for a
+ * structure or union always means to descend into the type. Both GCC 10 and
+ * Clang 8 already warn about these extra braces, nevertheless there is
+ * real-life code (the Postfix MTA) that exploits this corner case of the
+ * standard.
+ */
+struct point scalar_with_several_braces = {
+ {{{3}}},
+ {{{{4}}}},
+};
+
+struct rectangle {
+ struct point top_left;
+ struct point bottom_right;
+};
+
+/* C99 6.7.8p18 */
+struct rectangle screen = {
+ .bottom_right = {
+ 1920,
+ 1080,
+ }
+};
+
+/*
+ * C99 6.7.8p22 says: At the _end_ of its initializer list, the array no
+ * longer has incomplete type.
+ */
+struct point points[] = {
+ {
+ /*
+ * At this point, the size of the object 'points' is not known
+ * yet since its type is still incomplete. Lint could warn
+ * about this, but GCC and Clang already do.
+ *
+ * Before init.c 1.179 from 2021.03.30, the type information
+ * of 'points' was set too early, resulting in a negative
+ * array size below.
+ */
+ sizeof(int[-(int)sizeof(points)]),
+ 4
+ }
+};
+
+
+struct triangle {
+ struct point points[3];
+};
+
+struct pentagon {
+ struct point points[5];
+};
+
+struct geometry {
+ struct pentagon pentagons[6];
+ struct triangle triangles[10];
+ struct point points[3][5][2];
+};
+
+/*
+ * Initialization of a complex struct containing nested arrays and nested
+ * structs.
+ */
+struct geometry geometry = {
+ .pentagons[0].points[4].x = 1,
+ .points[0][0][0] = { 0, 0 },
+ .points[2][4][1] = {301, 302 },
+ /* expect+1: error: array subscript 3 cannot be > 2 [168] */
+ .points[3][0][0] = {3001, 3002 },
+ /* expect+1: error: array subscript 5 cannot be > 4 [168] */
+ .points[0][5][0] = {501, 502 },
+ /* expect+1: error: array subscript 2 cannot be > 1 [168] */
+ .points[0][0][2] = {21, 22 },
+};
+
+struct ends_with_unnamed_bit_field {
+ int member;
+ int:0;
+} ends_with_unnamed_bit_field = {
+ 12345,
+ /* expect+1: error: too many struct/union initializers [172] */
+ 23456,
+};
+
+char prefixed_message[] = {
+ 'E', ':', ' ',
+ /* expect+1: warning: invalid combination of integer 'char' and pointer 'pointer to char' for 'init' [183] */
+ "message\n",
+};
+
+char message_with_suffix[] = {
+ "message",
+ /* The excess character is not detected by lint but by compilers. */
+ '\n',
+};
+
+struct ten {
+ int i0;
+ int i1;
+ int i2;
+ int i3;
+ int i4;
+ int i5;
+ int i6;
+ int i7;
+ int i8;
+ int i9;
+};
+
+struct ten ten = {
+ .i3 = 3,
+ 4,
+ 5,
+ 6,
+};
+
+
+/*
+ * ISO C99 6.7.8 provides a large list of examples for initialization,
+ * covering all tricky edge cases.
+ */
+
+/* expect+1: warning: lossy conversion of 3.5 to 'int' [381] */
+int c99_6_7_8_p24_example1_i = 3.5;
+double _Complex c99_6_7_8_p24_example1_c = 5 + 3 * 1.0fi;
+
+int c99_6_7_8_p25_example2[] = { 1, 3, 5 };
+
+int c99_6_7_8_p26_example3a[4][3] = {
+ { 1, 3, 5 },
+ { 2, 4, 6 },
+ { 3, 5, 7 },
+};
+
+int c99_6_7_8_p26_example3b[4][3] = {
+ 1, 3, 5, 2, 4, 6, 3, 5, 7
+};
+
+int c99_6_7_8_p27_example4[4][3] = {
+ { 1 }, { 2 }, { 3 }, { 4 }
+};
+
+struct {
+ int a[3], b;
+} c99_6_7_8_p28_example5[] = {
+ { 1 },
+ 2,
+};
+
+short c99_6_7_8_p29_example6a[4][3][2] = {
+ { 1 },
+ { 2, 3 },
+ { 4, 5, 6 },
+};
+
+short c99_6_7_8_p29_example6b[4][3][2] = {
+ 1, 0, 0, 0, 0, 0,
+ 2, 3, 0, 0, 0, 0,
+ 4, 5, 6, 0, 0, 0,
+};
+
+short c99_6_7_8_p29_example6c[4][3][2] = {
+ {
+ { 1 },
+ },
+ {
+ { 2, 3 },
+ },
+ {
+ { 4, 5 },
+ { 6 },
+ }
+};
+
+void
+c99_6_7_8_p31_example7(void)
+{
+ typedef int A[];
+
+ A a = { 1, 2 }, b = { 3, 4, 5 };
+
+ /* expect+1: error: negative array dimension (-8) [20] */
+ typedef int reveal_sizeof_a[-(int)(sizeof(a))];
+ /* expect+1: error: negative array dimension (-12) [20] */
+ typedef int reveal_sizeof_b[-(int)(sizeof(b))];
+}
+
+char c99_6_7_8_p32_example8_s1[] = "abc",
+ c99_6_7_8_p32_example8_t1[3] = "abc";
+char c99_6_7_8_p32_example8_s2[] = { 'a', 'b', 'c', '\0' },
+ c99_6_7_8_p32_example8_t2[3] = { 'a', 'b', 'c' };
+char *c99_6_7_8_p32_example8_p = "abc";
+
+enum { member_one, member_two };
+const char *c99_6_7_8_p33_example9[] = {
+ [member_two] = "member two",
+ [member_one] = "member one",
+};
+
+struct {
+ int quot, rem;
+} c99_6_7_8_p34_example10 = { .quot = 2, .rem = -1 };
+
+struct { int a[3], b; } c99_6_7_8_p35_example11[] =
+ { [0].a = {1}, [1].a[0] = 2 };
+
+int c99_6_7_8_p36_example12a[16] = {
+ 1, 3, 5, 7, 9, [16-5] = 8, 6, 4, 2, 0
+};
+
+int c99_6_7_8_p36_example12b[8] = {
+ 1, 3, 5, 7, 9, [8-5] = 8, 6, 4, 2, 0
+};
+
+union {
+ int first_member;
+ void *second_member;
+ unsigned char any_member;
+} c99_6_7_8_p38_example13 = { .any_member = 42 };
+
+
+/*
+ * During initialization of an object of type array of unknown size, the type
+ * information on the symbol is updated in-place. Ensure that this happens on
+ * a copy of the type.
+ *
+ * C99 6.7.8p31 example 7
+ */
+void
+ensure_array_type_is_not_modified_during_initialization(void)
+{
+ typedef int array_of_unknown_size[];
+
+ array_of_unknown_size a1 = { 1, 2, 3};
+
+ switch (4) {
+ case sizeof(array_of_unknown_size):
+ /* expect+1: error: duplicate case '0' in switch [199] */
+ case 0:
+ case 3:
+ case 4:
+ case 12:
+ break;
+ }
+
+ /* expect+1: error: negative array dimension (-12) [20] */
+ typedef int reveal_sizeof_a1[-(int)(sizeof(a1))];
+}
+
+struct point unknown_member_name_beginning = {
+ /* expect+1: error: type 'struct point' does not have member 'r' [101] */
+ .r = 5,
+ .x = 4,
+ .y = 3,
+};
+
+struct point unknown_member_name_middle = {
+ .x = 4,
+ /* expect+1: error: type 'struct point' does not have member 'r' [101] */
+ .r = 5,
+ .y = 3,
+};
+
+struct point unknown_member_name_end = {
+ .x = 4,
+ .y = 3,
+ /* expect+1: error: type 'struct point' does not have member 'r' [101] */
+ .r = 5,
+};
+
+union value {
+ int int_value;
+ void *pointer_value;
+};
+
+union value unknown_union_member_name_first = {
+ /* expect+1: error: type 'union value' does not have member 'unknown_value' [101] */
+ .unknown_value = 4,
+ .int_value = 3,
+};
+
+union value unknown_union_member_name_second = {
+ .int_value = 3,
+ /* expect+1: error: type 'union value' does not have member 'unknown_value' [101] */
+ .unknown_value = 4,
+};
+
+struct point subscript_designator_on_struct = {
+ /* expect+1: error: syntax error 'designator '[...]' is only for arrays' [249] */
+ [0] = 3,
+};
+
+struct point unknown_member_on_struct = {
+ /* expect+1: error: type 'struct point' does not have member 'member' [101] */
+ .member[0][0].member = 4,
+};
+
+struct point unknown_member_on_scalar = {
+ /* expect+1: error: syntax error 'scalar type cannot use designator' [249] */
+ .x.y.z = 5,
+};
+
+struct {
+ int:16;
+ /* expect+2: warning: 'struct <unnamed>' has no named members [65] */
+ /* expect+1: error: cannot initialize struct/union with no named member [179] */
+} struct_with_only_unnamed_members = {
+ 123,
+};
+
+union {
+ int:16;
+ /* expect+2: warning: 'union <unnamed>' has no named members [65] */
+ /* expect+1: error: cannot initialize struct/union with no named member [179] */
+} union_with_only_unnamed_members = {
+ 123,
+};
+
+int designator_for_scalar = {
+ /* expect+1: error: syntax error 'scalar type cannot use designator' [249] */
+ .value = 3,
+};
+
+struct point member_designator_for_scalar_in_struct = {
+ /* expect+1: error: syntax error 'scalar type cannot use designator' [249] */
+ { .x = 3 },
+};
+struct point subscript_designator_for_scalar_in_struct = {
+ /* expect+1: error: syntax error 'designator '[...]' is only for arrays' [249] */
+ { [1] = 4 },
+};
+
+
+/* Seen in pcidevs_data.h, variable 'pci_words'. */
+const char string_initialized_with_braced_literal[] = {
+ "initializer",
+};
+
+// An array of unknown size containing strings.
+char weekday_names[][4] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+/* nested struct/union initialization */
+struct outer {
+ int i;
+ char c;
+ union inner {
+ short us;
+ char uc;
+ } u;
+ char *s;
+} struct_containing_union[] = {
+ {
+ .s = "foo",
+ .c = 'b',
+ .u = {
+ .uc = 'c'
+ }
+ },
+ {
+ .i = 1,
+ .c = 'a',
+ .u = {
+ .us = 2
+ }
+ },
+};
+
+/*
+ * The expansion of the offsetof macro may dereference a null pointer.
+ * Such expressions are allowed in initializers for objects with
+ * static duration.
+ */
+struct offset_and_data {
+ unsigned long offset;
+ unsigned long data;
+};
+
+struct offset_and_data offset_and_data = {
+ (unsigned long)&(((struct offset_and_data *)0)->data),
+ 0,
+};
+
+// The size of the array is determined by the maximum index, not by the last
+// one mentioned.
+int arr_11[] = { [10] = 10, [0] = 0 };
+/* expect+1: error: negative array dimension (-11) [20] */
+typedef int ctassert_11[-(int)(sizeof(arr_11) / sizeof(arr_11[0]))];
+
+// Without an explicit subscript designator, the subscript counts up.
+int arr_3[] = { [1] = 1, [0] = 0, 1, 2 };
+/* expect+1: error: negative array dimension (-3) [20] */
+typedef int ctassert_3[-(int)(sizeof(arr_3) / sizeof(arr_3[0]))];
+
+
+// C99 struct initialization using designators.
+struct {
+ int i;
+ char *s;
+} struct_array[] = {
+ {
+ .i = 2,
+ },
+ {
+ .s = "foo"
+ },
+ {
+ .i = 1,
+ .s = "bar"
+ },
+ {
+ .s = "foo",
+ .i = -1
+ },
+};
+
+// Ensure that deeply nested structs can be designated in an initializer.
+int
+init_deeply_nested_struct(void)
+{
+ struct rgb {
+ unsigned red;
+ unsigned green;
+ unsigned blue;
+ };
+
+ struct hobbies {
+ unsigned dancing: 1;
+ unsigned running: 1;
+ unsigned swimming: 1;
+ };
+
+ struct person {
+ struct hobbies hobbies;
+ struct rgb favorite_color;
+ };
+
+ struct city {
+ struct person mayor;
+ };
+
+ struct state {
+ struct city capital;
+ };
+
+ struct state st = {
+ .capital.mayor.hobbies.dancing = 1,
+ .capital.mayor.favorite_color.green = 0xFF,
+ .capital.mayor.favorite_color.red = 0xFF,
+ };
+ return st.capital.mayor.favorite_color.red;
+}
+
+struct {
+ int i[10];
+ char *s;
+} struct_array_with_inner_array[] = {
+ {
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
+ "foo"
+ },
+};
+
+struct {
+ int type;
+ union {
+ char b[20];
+ short s[10];
+ long l[5];
+ } data;
+} array_in_union_in_struct = {
+ .type = 3,
+ .data.l[0] = 4
+};
+
+// Somewhere between 2005.12.24.20.47.56 and 2006.12.19.19.06.44, lint could
+// not initialize named members, see PR bin/20264.
+union {
+ char *p;
+ int a[1];
+} array_in_union = {
+ .a = { 7 }
+};
+
+/*
+ * Initialization of a nested struct, in which some parts are initialized
+ * from non-constant expressions of the inner struct type.
+ *
+ * In C99, 6.7.8p13 describes exactly this case.
+ */
+void
+init_nested_struct(void)
+{
+
+ typedef enum O1 {
+ O1C = 101
+ } O1;
+ typedef enum O2 {
+ O2C = 102
+ } O2;
+ typedef enum O3 {
+ O3C = 103
+ } O3;
+ typedef enum I1 {
+ I1C = 201
+ } I1;
+ typedef enum I2 {
+ I2C = 202
+ } I2;
+
+ struct Inner1 {
+ I1 i1;
+ };
+
+ struct Outer3Inner1 {
+ O1 o1;
+ struct Inner1 inner;
+ O3 o3;
+ };
+
+ struct Inner1 inner1 = {
+ I1C
+ };
+ struct Outer3Inner1 o3i1 = {
+ O1C,
+ inner1,
+ O3C
+ };
+
+ O1 o1 = o3i1.o1;
+
+ struct Inner2 {
+ I1 i1;
+ I2 i2;
+ };
+
+ struct Outer3Inner2 {
+ O1 o1;
+ struct Inner2 inner;
+ O3 o3;
+ };
+
+ struct Inner2 inner2 = {
+ I1C,
+ I2C
+ };
+ struct Outer3Inner2 o3i2 = {
+ O1C,
+ inner2,
+ O3C
+ };
+ o1 = o3i2.o1;
+
+ /*
+ * For static storage duration, each initializer expression must be a
+ * constant expression.
+ */
+ static struct Inner2 inner3_static = {
+ I1C,
+ I2C
+ };
+ static struct Outer3Inner2 o3i2_static = {
+ O1C,
+ /* expect+1: error: non-constant initializer [177] */
+ inner3_static,
+ O3C
+ };
+}
diff --git a/usr.bin/xlint/lint1/lex_utf8.c b/usr.bin/xlint/lint1/lex_utf8.c
new file mode 100644
index 000000000000..1146d1eb9511
--- /dev/null
+++ b/usr.bin/xlint/lint1/lex_utf8.c
@@ -0,0 +1,12 @@
+/* $NetBSD: lex_utf8.c,v 1.3 2024/02/03 10:56:18 rillig Exp $ */
+# 3 "lex_utf8.c"
+
+/*
+ * Test lexing of multibyte characters and strings in a UTF-8 locale.
+ *
+ * See also:
+ * lex_wide_string.c (runs in the C locale)
+ */
+
+/* expect+1: error: negative array dimension (-3) [20] */
+typedef int mblen[-(int)(sizeof(L"Ä😄") / sizeof(L""))];
diff --git a/usr.bin/xlint/lint1/msg_079_nongcc.c b/usr.bin/xlint/lint1/msg_079_nongcc.c
new file mode 100644
index 000000000000..c0b9a50ce132
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_079_nongcc.c
@@ -0,0 +1,26 @@
+/* $NetBSD: msg_079_nongcc.c,v 1.1 2024/02/02 19:07:58 rillig Exp $ */
+# 3 "msg_079_nongcc.c"
+
+// Test for message: dubious escape \%c [79]
+
+/* \e is only accepted in GCC mode. */
+
+/* lint1-flags: -S -w -X 351 */
+
+/* expect+1: warning: dubious escape \e [79] */
+char char_e = '\e';
+/* expect+1: warning: dubious escape \y [79] */
+char char_y = '\y';
+/* expect+1: warning: dubious escape \e [79] */
+int wide_e = L'\e';
+/* expect+1: warning: dubious escape \y [79] */
+int wide_y = L'\y';
+
+/* expect+1: warning: dubious escape \e [79] */
+char char_string_e[] = "\e[0m";
+/* expect+1: warning: dubious escape \y [79] */
+char char_string_y[] = "\y[0m";
+/* expect+1: warning: dubious escape \e [79] */
+int wide_string_e[] = L"\e[0m";
+/* expect+1: warning: dubious escape \y [79] */
+int wide_string_y[] = L"\y[0m";
diff --git a/usr.bin/xlint/lint1/msg_118_ilp32.c b/usr.bin/xlint/lint1/msg_118_ilp32.c
new file mode 100644
index 000000000000..abd78681305b
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_118_ilp32.c
@@ -0,0 +1,47 @@
+/* $NetBSD: msg_118_ilp32.c,v 1.1 2025/09/06 20:18:41 rillig Exp $ */
+# 3 "msg_118_ilp32.c"
+
+/* Test for message: '%s' %s '%s' differs between traditional C and C90 [118] */
+
+/* lint1-flags: -hw -X 351 */
+/* lint1-only-if: ilp32 */
+
+int si;
+unsigned ui;
+long sl;
+unsigned long ul;
+
+/*
+ * On 32-bit platforms both operands of the '<<' operator are first promoted
+ * individually, and since C90 does not know 'long long', the maximum
+ * bit-size for an integer type is 32 bits.
+ */
+void
+test_shl(void)
+{
+ si <<= si;
+ si <<= ui;
+ si <<= sl;
+ si <<= ul;
+
+ si = si << si;
+ si = si << ui;
+ si = si << sl;
+ si = si << ul;
+}
+
+void
+test_shr(void)
+{
+ si >>= si;
+ si >>= ui;
+ si >>= sl;
+ si >>= ul;
+
+ si = si >> si;
+ /* expect+1: warning: 'int' >> 'unsigned int' differs between traditional C and C90 [118] */
+ si = si >> ui;
+ si = si >> sl;
+ /* expect+1: warning: 'int' >> 'unsigned long' differs between traditional C and C90 [118] */
+ si = si >> ul;
+}
diff --git a/usr.bin/xlint/lint1/msg_356.c b/usr.bin/xlint/lint1/msg_356.c
new file mode 100644
index 000000000000..d7dbd28f4d13
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_356.c
@@ -0,0 +1,35 @@
+/* $NetBSD: msg_356.c,v 1.3 2024/03/25 22:37:43 rillig Exp $ */
+# 3 "msg_356.c"
+
+// Test for message: short octal escape '%.*s' followed by digit '%c' [356]
+
+/* lint1-extra-flags: -X 351 */
+
+// When counting backwards in octal, the number before \040 is not \039 but
+// \037. This mistake sometimes happens when encoding the bit numbers for
+// snprintb(3) format conversions.
+
+char snprintb_fmt[] = "\020"
+ "\0040bit32" // 3-digit octal escapes are fine
+ "\0039bit31"
+ "\0038bit30"
+
+ "\040bit32"
+ /* expect+1: warning: short octal escape '\03' followed by digit '9' [356] */
+ "\039bit31"
+ /* expect+1: warning: short octal escape '\03' followed by digit '8' [356] */
+ "\038bit30"
+
+ "\40bit32"
+ /* expect+1: warning: short octal escape '\3' followed by digit '9' [356] */
+ "\39bit31"
+ /* expect+1: warning: short octal escape '\3' followed by digit '8' [356] */
+ "\38bit30"
+ "\37bit29"
+;
+
+char ok[] = ""
+ "\3\70" // short octal followed by escaped '8'
+ "\3\x38" // short octal followed by escaped '8'
+ "\3" "8" // short octal and '8' are separated
+;
diff --git a/usr.bin/xlint/lint1/msg_357.c b/usr.bin/xlint/lint1/msg_357.c
new file mode 100644
index 000000000000..e8f4d269076d
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_357.c
@@ -0,0 +1,57 @@
+/* $NetBSD: msg_357.c,v 1.3 2024/11/05 06:23:04 rillig Exp $ */
+# 3 "msg_357.c"
+
+// Test for message: hex escape '%.*s' mixes uppercase and lowercase digits [357]
+
+/*
+ * In the format argument of the snprintb and snprintb_m functions, a bit
+ * position or field width is written as an octal or hexadecimal escape
+ * sequence. If the description that follows a hexadecimal escape sequence
+ * starts with hexadecimal digits (A-Fa-f), these digits are still part of the
+ * escape sequence instead of the description.
+ *
+ * Since the escape sequences are typically written in lowercase and the
+ * descriptions are typically written in uppercase, a mixture of both cases
+ * indicates a mismatch.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+examples(unsigned u32, uint64_t u64)
+{
+ char buf[64];
+
+ /* expect+5: warning: hex escape '\x0aB' mixes uppercase and lowercase digits [357] */
+ /* expect+4: warning: hex escape '\x0aB' has more than 2 digits [358] */
+ /* expect+3: warning: bit position '\x0aB' (171) in '\x0aBIT' out of range 1..32 [371] */
+ snprintb(buf, sizeof(buf),
+ "\020\x0aBIT",
+ u32);
+
+ // This mismatch goes undetected as it has only 2 digits, does not mix
+ // case and is in bounds. A spellchecker could mark the unknown word
+ // 'ield' to give a hint.
+ snprintb(buf, sizeof(buf),
+ "\020\x1FIELD",
+ u32);
+
+ // If the input value is restricted further, the unintended hexadecimal
+ // escape sequence is detected, although with a less obvious message.
+ /* expect+3: warning: conversion '\x1FIELD' is unreachable by input value [378] */
+ snprintb(buf, sizeof(buf),
+ "\020\x1FIELD",
+ u32 & 0xffff);
+
+ /* expect+5: warning: hex escape '\x0aB' mixes uppercase and lowercase digits [357] */
+ /* expect+4: warning: hex escape '\x0aB' has more than 2 digits [358] */
+ /* expect+3: warning: bit position '\x0aB' (171) in 'b\x0aBIT\0' out of range 0..63 [371] */
+ snprintb(buf, sizeof(buf),
+ "\177\020b\x0aBIT\0",
+ u64);
+}
diff --git a/usr.bin/xlint/lint1/msg_358.c b/usr.bin/xlint/lint1/msg_358.c
new file mode 100644
index 000000000000..3e3799ff07a2
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_358.c
@@ -0,0 +1,65 @@
+/* $NetBSD: msg_358.c,v 1.4 2024/11/05 06:23:04 rillig Exp $ */
+# 3 "msg_358.c"
+
+// Test for message: hex escape '%.*s' has more than 2 digits [358]
+
+/*
+ * In the format argument of the snprintb and snprintb_m functions, a bit
+ * position or field width is written as an octal or hexadecimal escape
+ * sequence. If the description that follows a hexadecimal escape sequence
+ * starts with hexadecimal digits (A-Fa-f), these digits are still part of the
+ * escape sequence instead of the description.
+ *
+ * All platforms supported by lint have 8-bit char, so using more than the
+ * maximum necessary 2 hexadecimal digits in an escape sequence is suspicious
+ * of being unintended.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+examples(unsigned u32, uint64_t u64)
+{
+ char buf[64];
+
+ /* expect+3: warning: hex escape '\x01B' has more than 2 digits [358] */
+ snprintb(buf, sizeof(buf),
+ "\020\x01BIT",
+ u32);
+
+ /* expect+3: warning: hex escape '\x01b' has more than 2 digits [358] */
+ snprintb(buf, sizeof(buf),
+ "\020\x01bit",
+ u32);
+
+ // This mismatch goes undetected as it has only 2 digits, does not mix
+ // case and is in bounds. A spellchecker could mark the unknown word
+ // 'ield' to give a hint.
+ snprintb(buf, sizeof(buf),
+ "\020\x1FIELD",
+ u32);
+
+ /* expect+3: warning: hex escape '\x01b' has more than 2 digits [358] */
+ snprintb(buf, sizeof(buf),
+ "\177\020b\x01bit\0",
+ u64);
+
+ /* expect+3: warning: hex escape '\x02b' has more than 2 digits [358] */
+ snprintb(buf, sizeof(buf),
+ "\177\020f\x00\x02bit\0",
+ u64);
+
+ // In this example from the snprintb manual page, the descriptions
+ // that start with a hexadecimal digit must be separated from the
+ // hexadecimal escape sequence for the bit position.
+ snprintb(buf, sizeof(buf),
+ "\20\x10NOTBOOT\x0f" "FPP\x0eSDVMA\x0cVIDEO"
+ "\x0bLORES\x0a" "FPA\x09" "DIAG\x07" "CACHE"
+ "\x06IOCACHE\x05LOOPBACK\x04" "DBGCACHE",
+ u32);
+}
diff --git a/usr.bin/xlint/lint1/msg_359.c b/usr.bin/xlint/lint1/msg_359.c
new file mode 100644
index 000000000000..eb26e6093f19
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_359.c
@@ -0,0 +1,29 @@
+/* $NetBSD: msg_359.c,v 1.3 2024/11/05 06:23:04 rillig Exp $ */
+# 3 "msg_359.c"
+
+// Test for message: missing new-style '\177' or old-style number base [359]
+
+/*
+ * The first or second character of the snprintb format specifies the number
+ * base. It must be an octal or hexadecimal escape sequence, as the characters
+ * 2, 10 and 16 are not printable, and writing '\n' instead of '\x0a' would be
+ * misleading.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+old_style_number_base(void)
+{
+ char buf[64];
+
+ /* expect+1: warning: missing new-style '\177' or old-style number base [359] */
+ snprintb(buf, sizeof(buf), "", 0);
+ snprintb(buf, sizeof(buf), "\010", 0);
+ snprintb(buf, sizeof(buf), "" "\177\020" "", 0);
+}
diff --git a/usr.bin/xlint/lint1/msg_360.c b/usr.bin/xlint/lint1/msg_360.c
new file mode 100644
index 000000000000..dc3f208a36a7
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_360.c
@@ -0,0 +1,28 @@
+/* $NetBSD: msg_360.c,v 1.4 2024/11/05 06:23:04 rillig Exp $ */
+# 3 "msg_360.c"
+
+// Test for message: missing new-style number base after '\177' [360]
+
+/*
+ * The new-style format requires the number base as the second character.
+ * This check is merely a byproduct of the implementation, it provides little
+ * value of its own.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+new_style_number_base(void)
+{
+ char buf[64];
+
+ /* expect+1: warning: missing new-style number base after '\177' [360] */
+ snprintb(buf, sizeof(buf), "\177", 0);
+ /* expect+1: warning: number base '\002' is 2, must be 8, 10 or 16 [361] */
+ snprintb(buf, sizeof(buf), "\177\002", 0);
+}
diff --git a/usr.bin/xlint/lint1/msg_361.c b/usr.bin/xlint/lint1/msg_361.c
new file mode 100644
index 000000000000..08b3c733ccb2
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_361.c
@@ -0,0 +1,52 @@
+/* $NetBSD: msg_361.c,v 1.4 2024/11/05 06:23:04 rillig Exp $ */
+# 3 "msg_361.c"
+
+// Test for message: number base '%.*s' is %ju, must be 8, 10 or 16 [361]
+
+/*
+ * The first or second character of the snprintb format specifies the number
+ * base. It must be given as an octal or hexadecimal escape sequence.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+old_style_number_base(void)
+{
+ char buf[64];
+
+ /* expect+1: warning: missing new-style '\177' or old-style number base [359] */
+ snprintb(buf, sizeof(buf), "", 0);
+ /* expect+1: warning: number base '\002' is 2, must be 8, 10 or 16 [361] */
+ snprintb(buf, sizeof(buf), "\002", 0);
+ snprintb(buf, sizeof(buf), "\010", 0);
+ snprintb(buf, sizeof(buf), "\n", 0);
+ snprintb(buf, sizeof(buf), "\020", 0);
+ /* expect+1: warning: number base '\014' is 12, must be 8, 10 or 16 [361] */
+ snprintb(buf, sizeof(buf), "" "\014" "", 0);
+ snprintb(buf, sizeof(buf), "" "\020" "", 0);
+}
+
+void
+new_style_number_base(void)
+{
+ char buf[64];
+
+ /* expect+1: warning: missing new-style number base after '\177' [360] */
+ snprintb(buf, sizeof(buf), "\177", 0);
+ /* expect+1: warning: number base '\0' is 0, must be 8, 10 or 16 [361] */
+ snprintb(buf, sizeof(buf), "\177\0", 0);
+ /* expect+1: warning: number base '\002' is 2, must be 8, 10 or 16 [361] */
+ snprintb(buf, sizeof(buf), "\177\002", 0);
+ snprintb(buf, sizeof(buf), "\177\010", 0);
+ snprintb(buf, sizeof(buf), "\177\n", 0);
+ snprintb(buf, sizeof(buf), "\177\020", 0);
+ /* expect+1: warning: number base '\014' is 12, must be 8, 10 or 16 [361] */
+ snprintb(buf, sizeof(buf), "" "\177\014" "", 0);
+ snprintb(buf, sizeof(buf), "" "\177\020" "", 0);
+}
diff --git a/usr.bin/xlint/lint1/msg_362.c b/usr.bin/xlint/lint1/msg_362.c
new file mode 100644
index 000000000000..58da2bd219e2
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_362.c
@@ -0,0 +1,33 @@
+/* $NetBSD: msg_362.c,v 1.4 2024/08/31 06:57:31 rillig Exp $ */
+# 3 "msg_362.c"
+
+// Test for message: conversion '%.*s' should not be escaped [362]
+
+/*
+ * Since the characters used for the conversion type were chosen to be easily
+ * readable, it doesn't make sense to obfuscate them.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+example(unsigned u32)
+{
+ char buf[64];
+
+ /* expect+9: warning: conversion '\142' should not be escaped [362] */
+ /* expect+8: warning: bit position 'o' in '\142old-style-lsb\0' should be escaped as octal or hex [369] */
+ /* expect+7: warning: bit position 'o' (111) in '\142old-style-lsb\0' out of range 0..63 [371] */
+ /* expect+6: warning: unknown conversion '\001', must be one of 'bfF=:*' [374] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "\142old-style-lsb\0"
+ "\001old-style-lsb\0"
+ "\142\000old-style-lsb\0",
+ u32);
+}
diff --git a/usr.bin/xlint/lint1/msg_363.c b/usr.bin/xlint/lint1/msg_363.c
new file mode 100644
index 000000000000..41a0e06845f2
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_363.c
@@ -0,0 +1,64 @@
+/* $NetBSD: msg_363.c,v 1.7 2024/11/05 06:23:04 rillig Exp $ */
+# 3 "msg_363.c"
+
+// Test for message: escaped character '%.*s' in description of conversion '%.*s' [363]
+
+/*
+ * The purpose of snprintb is to produce a printable, visible representation
+ * of a binary number, therefore the description should consist of simple
+ * characters only, and these should not need to be escaped. If they are,
+ * it's often due to a typo, such as a missing terminating '\0'.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+old_style_description(unsigned u32)
+{
+ char buf[64];
+
+ /* expect+6: warning: bit position '\t' in '\tprint' should be escaped as octal or hex [369] */
+ /* expect+5: warning: escaped character '\377' in description of conversion '\nable\377' [363] */
+ /* expect+4: warning: bit position '\n' in '\nable\377' should be escaped as octal or hex [369] */
+ snprintb(buf, sizeof(buf),
+ "\020"
+ "\001non\tprint\nable\377",
+ u32);
+
+ // In the new format, the description can technically contain
+ // arbitrary characters, but having non-printable characters would
+ // produce confusing output, so any escaped characters are suspicious
+ // of being unintended.
+ /* expect+6: warning: escaped character '\t' in description of conversion 'b\000non\t' [363] */
+ /* expect+5: warning: escaped character '\n' in description of conversion 'b\000non\tprint\n' [363] */
+ /* expect+4: warning: escaped character '\377' in description of conversion 'b\000non\tprint\nable\377' [363] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "b\000non\tprint\nable\377\0",
+ u32);
+
+ /* expect+10: warning: escaped character '\177' in description of conversion '\002""\177' [363] */
+ /* expect+9: warning: escaped character '\177' in description of conversion '\003aa""""\177' [363] */
+ /* expect+8: warning: escaped character '\177' in description of conversion '\004""bb""\177' [363] */
+ /* expect+7: warning: escaped character '\177' in description of conversion '\005""""cc\177' [363] */
+ snprintb(buf, sizeof(buf),
+ "\020"
+ "\002""\177"
+ "\003aa""""\177"
+ "\004""bb""\177"
+ "\005""""cc\177",
+ u32);
+
+ /* expect+6: warning: bit position '\000' (0) in '\000print' out of range 1..32 [371] */
+ /* expect+5: warning: bit position '\n' in '\nable' should be escaped as octal or hex [369] */
+ /* expect+4: warning: redundant '\0' at the end of the format [377] */
+ snprintb(buf, sizeof(buf),
+ "\020"
+ "\001non\000print\nable\0",
+ u32);
+}
diff --git a/usr.bin/xlint/lint1/msg_364.c b/usr.bin/xlint/lint1/msg_364.c
new file mode 100644
index 000000000000..61e24d4aa235
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_364.c
@@ -0,0 +1,35 @@
+/* $NetBSD: msg_364.c,v 1.4 2024/08/31 06:57:31 rillig Exp $ */
+# 3 "msg_364.c"
+
+// Test for message: missing bit position after '%.*s' [364]
+
+/*
+ * The conversions 'b', 'f' and 'F' require a bit position as their first
+ * argument.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+example(unsigned u32)
+{
+ char buf[64];
+
+ /* expect+4: warning: missing bit position after 'b' [364] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "b",
+ u32);
+
+ /* expect+5: warning: empty description in 'b\007' [367] */
+ /* expect+4: warning: missing '\0' at the end of 'b\007' [366] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "b\007",
+ u32);
+}
diff --git a/usr.bin/xlint/lint1/msg_365.c b/usr.bin/xlint/lint1/msg_365.c
new file mode 100644
index 000000000000..f77fdc121a77
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_365.c
@@ -0,0 +1,34 @@
+/* $NetBSD: msg_365.c,v 1.4 2024/08/31 06:57:31 rillig Exp $ */
+# 3 "msg_365.c"
+
+// Test for message: missing field width after '%.*s' [365]
+
+/*
+ * The conversions 'f' and 'F' require a field width as their second argument.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+example(unsigned u32)
+{
+ char buf[64];
+
+ /* expect+4: warning: missing field width after 'f\000' [365] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "f\000",
+ u32);
+
+ /* expect+5: warning: empty description in 'f\007\010' [367] */
+ /* expect+4: warning: missing '\0' at the end of 'f\007\010' [366] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "f\007\010",
+ u32);
+}
diff --git a/usr.bin/xlint/lint1/msg_366.c b/usr.bin/xlint/lint1/msg_366.c
new file mode 100644
index 000000000000..d6fd1884b302
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_366.c
@@ -0,0 +1,70 @@
+/* $NetBSD: msg_366.c,v 1.5 2024/08/31 06:57:31 rillig Exp $ */
+# 3 "msg_366.c"
+
+// Test for message: missing '\0' at the end of '%.*s' [366]
+
+/*
+ * In the new-style format, each conversion ends with a '\0'. If that's not
+ * the case, snprintb will read beyond the end of the format argument, looking
+ * for the terminating '\0'. In the most common case where the format comes
+ * from a string literal, the '\0' from the conversion needs to be spelled
+ * out, while the '\0' that terminates the sequence of conversions is provided
+ * by the C compiler.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+example(unsigned u32)
+{
+ char buf[64];
+
+ /* expect+4: warning: redundant '\0' at the end of the format [377] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "\0",
+ u32);
+
+ /* expect+5: warning: empty description in 'b\007' [367] */
+ /* expect+4: warning: missing '\0' at the end of 'b\007' [366] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "b\007",
+ u32);
+
+ /* expect+5: warning: empty description in 'f\007\000' [367] */
+ /* expect+4: warning: missing '\0' at the end of 'f\007\000' [366] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "f\007\000",
+ u32);
+
+ /* expect+4: warning: missing '\0' at the end of 'F\007\000' [366] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "F\007\000",
+ u32);
+
+ /* expect+4: warning: missing '\0' at the end of '=\007value' [366] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "=\007value",
+ u32);
+
+ /* expect+4: warning: missing '\0' at the end of ':\007value' [366] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ ":\007value",
+ u32);
+
+ /* expect+4: warning: missing '\0' at the end of '*default' [366] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "*default",
+ u32);
+}
diff --git a/usr.bin/xlint/lint1/msg_367.c b/usr.bin/xlint/lint1/msg_367.c
new file mode 100644
index 000000000000..5965098443e6
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_367.c
@@ -0,0 +1,120 @@
+/* $NetBSD: msg_367.c,v 1.4 2025/08/31 20:43:27 rillig Exp $ */
+# 3 "msg_367.c"
+
+// Test for message: empty description in '%.*s' [367]
+
+/*
+ * Each bit or field or comparison value gets a description. If such a
+ * description is empty, the generated output will contain empty angle
+ * brackets or multiple adjacent commas or commas adjacent to an angle
+ * bracket, such as '<,,,,>'.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+old_style(unsigned u32)
+{
+ char buf[64];
+
+ /* expect+10: warning: empty description in '\001' [367] */
+ /* expect+9: warning: empty description in '\002' [367] */
+ /* expect+8: warning: empty description in '\003' [367] */
+ /* expect+7: warning: empty description in '\004' [367] */
+ snprintb(buf, sizeof(buf),
+ "\020"
+ "\001"
+ "\002"
+ "\003"
+ "\004",
+ u32);
+
+ /* expect+10: warning: empty description in '\001' [367] */
+ /* expect+9: warning: empty description in '\002' [367] */
+ /* expect+8: warning: empty description in '\003' [367] */
+ /* expect+7: warning: empty description in '\004' [367] */
+ snprintb(buf, sizeof(buf),
+ "\020"
+ "\001" "" ""
+ "\002" "" ""
+ "\003" "" ""
+ "\004" "" "",
+ u32);
+
+ // Single-letter descriptions are not empty.
+ snprintb(buf, sizeof(buf),
+ "\020"
+ "\001a"
+ "\002b"
+ "\003c"
+ "\004d",
+ u32);
+}
+
+void
+new_style(uint64_t u64)
+{
+ char buf[64];
+
+ /* expect+4: warning: empty description in 'b\000\0' [367] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "b\000\0",
+ u64);
+
+ /* expect+4: warning: empty description in 'f\000\010\0' [367] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "f\000\010\0",
+ u64);
+
+ // No warning, as 'F' does not take a description.
+ // If there were a description, it would simply be skipped.
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "F\000\010\0",
+ u64);
+
+ /* expect+4: warning: empty description in '=\000\0' [367] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "=\000\0",
+ u64);
+
+ /* expect+4: warning: empty description in ':\000\0' [367] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ ":\000\0",
+ u64);
+
+ /* expect+4: warning: empty description in '*\0' [367] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "*\0",
+ u64);
+
+ // Single-letter descriptions are not empty.
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "b\000b\0"
+ "f\001\001f\0"
+ "F\002\002F\0"
+ "=\000z\0"
+ ":\001o\0"
+ "*d\0",
+ /* expect+1: warning: conversion '=' does not mix with 'F' [386] */
+ u64 >> 1);
+
+ /* expect+6: warning: empty description in 'b\001""""""\0' [367] */
+ /* expect+5: warning: empty description in 'b\003""""""\0' [367] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "b\001" "" "" "\0"
+ "b\003" "" "" "\0",
+ u64);
+}
diff --git a/usr.bin/xlint/lint1/msg_368.c b/usr.bin/xlint/lint1/msg_368.c
new file mode 100644
index 000000000000..fd752f7cbbbf
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_368.c
@@ -0,0 +1,34 @@
+/* $NetBSD: msg_368.c,v 1.3 2024/08/31 06:57:31 rillig Exp $ */
+# 3 "msg_368.c"
+
+// Test for message: missing comparison value after conversion '%.*s' [368]
+
+/*
+ * The conversions '=' and ':' require a comparison value as their argument,
+ * followed by the description and the terminating null character.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+example(uint64_t val)
+{
+ char buf[64];
+
+ /* expect+4: warning: missing comparison value after conversion '=' [368] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "=",
+ val);
+
+ /* expect+4: warning: missing comparison value after conversion ':' [368] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ ":",
+ val);
+}
diff --git a/usr.bin/xlint/lint1/msg_369.c b/usr.bin/xlint/lint1/msg_369.c
new file mode 100644
index 000000000000..8129dc7b359b
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_369.c
@@ -0,0 +1,52 @@
+/* $NetBSD: msg_369.c,v 1.3 2024/08/31 06:57:31 rillig Exp $ */
+# 3 "msg_369.c"
+
+// Test for message: bit position '%.*s' in '%.*s' should be escaped as octal or hex [369]
+
+/*
+ * To distinguish bit positions from the description text, they should use
+ * octal or hex escape sequences. Of these, octal escape sequences are less
+ * error-prone, as they consist of at most 3 octal digits, whereas hex escape
+ * sequences consume as many digits as available.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+example(unsigned u32, uint64_t u64)
+{
+ char buf[64];
+
+ /* expect+8: warning: bit position ' ' in ' space' should be escaped as octal or hex [369] */
+ /* expect+7: warning: bit position '\t' in '\ttab' should be escaped as octal or hex [369] */
+ /* expect+6: warning: bit position '\n' in '\nnewline' should be escaped as octal or hex [369] */
+ snprintb(buf, sizeof(buf),
+ "\020"
+ " space"
+ "\ttab"
+ "\nnewline",
+ u32);
+
+ /* expect+8: warning: bit position ' ' in 'b space\0' should be escaped as octal or hex [369] */
+ /* expect+7: warning: bit position '\t' in 'b\ttab\0' should be escaped as octal or hex [369] */
+ /* expect+6: warning: bit position '\n' in 'b\nnewline\0' should be escaped as octal or hex [369] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "b space\0"
+ "b\ttab\0"
+ "b\nnewline\0",
+ u64);
+
+ /* expect+6: warning: bit position '\t' in 'f\t\001tab\0' should be escaped as octal or hex [369] */
+ /* expect+5: warning: bit position '\n' in 'F\n\001newline\0' should be escaped as octal or hex [369] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "f\t\001tab\0"
+ "F\n\001newline\0",
+ u64);
+}
diff --git a/usr.bin/xlint/lint1/msg_370.c b/usr.bin/xlint/lint1/msg_370.c
new file mode 100644
index 000000000000..e8c762c8deec
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_370.c
@@ -0,0 +1,52 @@
+/* $NetBSD: msg_370.c,v 1.3 2024/08/31 06:57:31 rillig Exp $ */
+# 3 "msg_370.c"
+
+// Test for message: field width '%.*s' in '%.*s' should be escaped as octal or hex [370]
+
+/*
+ * To distinguish field widths from the description text, they should use
+ * octal or hex escape sequences. Of these, octal escape sequences are less
+ * error-prone, as they consist of at most 3 octal digits, whereas hex escape
+ * sequences consume as many digits as available.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+example(uint64_t u64)
+{
+ char buf[64];
+
+ /* expect+11: warning: bit position ' ' in 'f space\0' should be escaped as octal or hex [369] */
+ /* expect+10: warning: field width ' ' in 'f space\0' should be escaped as octal or hex [370] */
+ /* expect+9: warning: bit position '\t' in 'f\t\ttab\0' should be escaped as octal or hex [369] */
+ /* expect+8: warning: field width '\t' in 'f\t\ttab\0' should be escaped as octal or hex [370] */
+ /* expect+7: warning: bit position '\n' in 'f\n\nnewline\0' should be escaped as octal or hex [369] */
+ /* expect+6: warning: field width '\n' in 'f\n\nnewline\0' should be escaped as octal or hex [370] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "f space\0"
+ "f\t\ttab\0"
+ "f\n\nnewline\0",
+ u64);
+ /* expect-1: warning: 'f\n\nnewline\0' overlaps earlier 'f\t\ttab\0' on bit 10 [376] */
+
+ /* expect+11: warning: bit position ' ' in 'F space\0' should be escaped as octal or hex [369] */
+ /* expect+10: warning: field width ' ' in 'F space\0' should be escaped as octal or hex [370] */
+ /* expect+9: warning: bit position '\t' in 'F\t\ttab\0' should be escaped as octal or hex [369] */
+ /* expect+8: warning: field width '\t' in 'F\t\ttab\0' should be escaped as octal or hex [370] */
+ /* expect+7: warning: bit position '\n' in 'F\n\nnewline\0' should be escaped as octal or hex [369] */
+ /* expect+6: warning: field width '\n' in 'F\n\nnewline\0' should be escaped as octal or hex [370] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "F space\0"
+ "F\t\ttab\0"
+ "F\n\nnewline\0",
+ u64);
+ /* expect-1: warning: 'F\n\nnewline\0' overlaps earlier 'F\t\ttab\0' on bit 10 [376] */
+}
diff --git a/usr.bin/xlint/lint1/msg_371.c b/usr.bin/xlint/lint1/msg_371.c
new file mode 100644
index 000000000000..cb6ffdd54f20
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_371.c
@@ -0,0 +1,62 @@
+/* $NetBSD: msg_371.c,v 1.3 2024/08/31 06:57:31 rillig Exp $ */
+# 3 "msg_371.c"
+
+// Test for message: bit position '%.*s' (%ju) in '%.*s' out of range %u..%u [371]
+
+/*
+ * In old-style formats, bit positions are 1-based and must be in the range
+ * from 1 to 32. In new-style formats, bit positions are 0-based and must be
+ * in the range from 0 to 63.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+example(unsigned u32, uint64_t u64)
+{
+ char buf[64];
+
+ /* expect+12: warning: bit position '\000' (0) in '\000zero' out of range 1..32 [371] */
+ /* expect+11: warning: escaped character '\041' in description of conversion '\040bit32""\041' [363] */
+ /* expect+10: warning: escaped character '\177' in description of conversion '\040bit32""\041bit33""\177' [363] */
+ /* expect+9: warning: escaped character '\377' in description of conversion '\040bit32""\041bit33""\177bit127""\377' [363] */
+ snprintb(buf, sizeof(buf),
+ "\020"
+ "\000zero"
+ "\001bit1"
+ "\040bit32"
+ "\041bit33"
+ "\177bit127"
+ "\377bit255",
+ u32);
+
+ /* expect+10: warning: bit position '\100' (64) in 'b\100bit64\0' out of range 0..63 [371] */
+ /* expect+9: warning: bit position '\177' (127) in 'b\177bit127\0' out of range 0..63 [371] */
+ /* expect+8: warning: bit position '\377' (255) in 'b\377bit255\0' out of range 0..63 [371] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "b\000bit0\0"
+ "b\077bit63\0"
+ "b\100bit64\0"
+ "b\177bit127\0"
+ "b\377bit255\0",
+ u64);
+
+ /* expect+11: warning: bit position '\100' (64) in 'F\100\000none\0' out of range 0..63 [371] */
+ /* expect+10: warning: bit position '\100' (64) in 'f\100\001oob\0' out of range 0..63 [371] */
+ /* expect+9: warning: bit field end 65 in 'f\100\001oob\0' out of range 0..64 [373] */
+ /* expect+8: warning: bit position '\101' (65) in 'F\101\001oob\0' out of range 0..63 [371] */
+ /* expect+7: warning: bit field end 66 in 'F\101\001oob\0' out of range 0..64 [373] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "f\077\001msb\0"
+ "F\100\000none\0"
+ "f\100\001oob\0"
+ "F\101\001oob\0",
+ u64);
+}
diff --git a/usr.bin/xlint/lint1/msg_372.c b/usr.bin/xlint/lint1/msg_372.c
new file mode 100644
index 000000000000..b4c914d75f64
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_372.c
@@ -0,0 +1,36 @@
+/* $NetBSD: msg_372.c,v 1.4 2024/08/31 06:57:31 rillig Exp $ */
+# 3 "msg_372.c"
+
+// Test for message: field width '%.*s' (%ju) in '%.*s' out of range 0..64 [372]
+
+/*
+ * In new-style formats, the width of a bit-field must be between 0 (an empty
+ * bit-field) and 64 (a bit-field spanning the whole value).
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+example(uint64_t u64)
+{
+ char buf[64];
+
+ /* expect+12: warning: field width '\101' (65) in 'f\000\101all+1\0' out of range 0..64 [372] */
+ /* expect+11: warning: bit field end 65 in 'f\000\101all+1\0' out of range 0..64 [373] */
+ /* expect+10: warning: bit field end 65 in 'f\001\100oob64\0' out of range 0..64 [373] */
+ /* expect+9: warning: 'f\001\100oob64\0' overlaps earlier 'f\000\100all\0' on bit 1 [376] */
+ /* expect+8: warning: field width '\377' (255) in 'f\010\377oob64\0' out of range 0..64 [372] */
+ /* expect+7: warning: bit field end 263 in 'f\010\377oob64\0' out of range 0..64 [373] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "f\000\100all\0"
+ "f\000\101all+1\0"
+ "f\001\100oob64\0"
+ "f\010\377oob64\0",
+ u64);
+}
diff --git a/usr.bin/xlint/lint1/msg_373.c b/usr.bin/xlint/lint1/msg_373.c
new file mode 100644
index 000000000000..5eded6ff9e02
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_373.c
@@ -0,0 +1,44 @@
+/* $NetBSD: msg_373.c,v 1.4 2024/08/31 06:57:31 rillig Exp $ */
+# 3 "msg_373.c"
+
+// Test for message: bit field end %ju in '%.*s' out of range 0..64 [373]
+
+/*
+ * A bit-field may start in the middle of the value. When its end goes beyond
+ * 64, this means the uppermost bits will always be 0, and a narrower
+ * bit-field would have the same effect.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+example(uint64_t u64)
+{
+ char buf[64];
+
+ /* expect+12: warning: field width '\101' (65) in 'f\000\101all+1\0' out of range 0..64 [372] */
+ /* expect+11: warning: bit field end 65 in 'f\000\101all+1\0' out of range 0..64 [373] */
+ /* expect+10: warning: bit field end 65 in 'f\001\100oob64\0' out of range 0..64 [373] */
+ /* expect+9: warning: 'f\001\100oob64\0' overlaps earlier 'f\000\100all\0' on bit 1 [376] */
+ /* expect+8: warning: field width '\377' (255) in 'f\010\377oob64\0' out of range 0..64 [372] */
+ /* expect+7: warning: bit field end 263 in 'f\010\377oob64\0' out of range 0..64 [373] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "f\000\100all\0"
+ "f\000\101all+1\0"
+ "f\001\100oob64\0"
+ "f\010\377oob64\0",
+ u64);
+
+ /* expect+5: warning: bit position '\377' (255) in 'f\377\002wrap-around\0' out of range 0..63 [371] */
+ /* expect+4: warning: bit field end 257 in 'f\377\002wrap-around\0' out of range 0..64 [373] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "f\377\002wrap-around\0",
+ u64);
+}
diff --git a/usr.bin/xlint/lint1/msg_374.c b/usr.bin/xlint/lint1/msg_374.c
new file mode 100644
index 000000000000..1d4312623e58
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_374.c
@@ -0,0 +1,49 @@
+/* $NetBSD: msg_374.c,v 1.6 2024/08/31 06:57:31 rillig Exp $ */
+# 3 "msg_374.c"
+
+// Test for message: unknown conversion '%.*s', must be one of 'bfF=:*' [374]
+
+/*
+ * In the new-style format, an unknown conversion is assumed to have a single
+ * argument, followed by a null-terminated description.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+example(uint64_t u64)
+{
+ char buf[64];
+
+ /* expect+4: warning: unknown conversion 'x', must be one of 'bfF=:*' [374] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "x12345\0",
+ u64);
+
+ /* expect+4: warning: unknown conversion '\000', must be one of 'bfF=:*' [374] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "\00012345\0",
+ u64);
+
+ /* expect+5: warning: redundant '\0' at the end of the format [377] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "b\00012345\0"
+ "\0",
+ u64);
+
+ // Real-life example: the '\b' is a typo.
+ //
+ /* expect+4: warning: unknown conversion '\b', must be one of 'bfF=:*' [374] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "b\15ENCNT\0b\16" "TC\0\b\20DSBL_CSR_DRN\0",
+ u64);
+}
diff --git a/usr.bin/xlint/lint1/msg_375.c b/usr.bin/xlint/lint1/msg_375.c
new file mode 100644
index 000000000000..f8251c101d71
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_375.c
@@ -0,0 +1,38 @@
+/* $NetBSD: msg_375.c,v 1.4 2024/08/31 06:57:31 rillig Exp $ */
+# 3 "msg_375.c"
+
+// Test for message: comparison value '%.*s' (%ju) exceeds maximum field value %ju [375]
+
+/*
+ * When a bit field can take the values 0 to 15, there is no point comparing
+ * it to 16.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+example(uint64_t u64)
+{
+ char buf[64];
+
+ /* expect+14: warning: comparison value '\020' (16) exceeds maximum field value 15 [375] */
+ /* expect+13: warning: comparison value '\377' (255) exceeds maximum field value 15 [375] */
+ /* expect+12: warning: comparison value '\020' (16) exceeds maximum field value 15 [375] */
+ /* expect+11: warning: comparison value '\377' (255) exceeds maximum field value 15 [375] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "f\000\004low\0"
+ "=\01715\0"
+ "=\02016\0"
+ "=\37716\0"
+ "F\004\004low\0"
+ ":\01715\0"
+ ":\02016\0"
+ ":\37716\0",
+ u64);
+}
diff --git a/usr.bin/xlint/lint1/msg_376.c b/usr.bin/xlint/lint1/msg_376.c
new file mode 100644
index 000000000000..2e2135e869ae
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_376.c
@@ -0,0 +1,58 @@
+/* $NetBSD: msg_376.c,v 1.4 2024/08/31 06:57:31 rillig Exp $ */
+# 3 "msg_376.c"
+
+// Test for message: '%.*s' overlaps earlier '%.*s' on bit %u [376]
+
+/*
+ * When bits and fields overlap, it's often due to typos or off-by-one errors.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+example(unsigned u32, uint64_t u64)
+{
+ char buf[64];
+
+ // In the old-style format, bit positions are 1-based.
+ snprintb(buf, sizeof(buf),
+ "\020"
+ "\001lsb"
+ "\x01lsb"
+ "\040msb"
+ "\x20msb"
+ "\041oob"
+ "\x21oob",
+ /* expect+4: warning: '\x01lsb' overlaps earlier '\001lsb' on bit 1 [376] */
+ /* expect+3: warning: escaped character '\041' in description of conversion '\x20msb""\041' [363] */
+ /* expect+2: warning: escaped character '\x21' in description of conversion '\x20msb""\041oob""\x21' [363] */
+ /* expect+1: warning: '\x20msb""\041oob""\x21oob' overlaps earlier '\040msb' on bit 32 [376] */
+ u32);
+
+ // In the new-style format, bit positions are 0-based.
+ /* expect+10: warning: 'b\x00lsb\0' overlaps earlier 'b\000lsb\0' on bit 0 [376] */
+ /* expect+9: warning: 'b\x3fmsb\0' overlaps earlier 'b\077msb\0' on bit 63 [376] */
+ /* expect+8: warning: bit position '\x40' (64) in 'b\x40oob\0' out of range 0..63 [371] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "b\000lsb\0"
+ "b\x00lsb\0"
+ "b\077msb\0"
+ "b\x3fmsb\0"
+ "b\x40oob\0",
+ u64);
+
+ /* expect+7: warning: 'F\014\010f2\0' overlaps earlier 'f\010\010f1\0' on bit 12 [376] */
+ /* expect+6: warning: 'f\020\010f3\0' overlaps earlier 'F\014\010f2\0' on bit 16 [376] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "f\010\010f1\0"
+ "F\014\010f2\0"
+ "f\020\010f3\0",
+ u64);
+}
diff --git a/usr.bin/xlint/lint1/msg_377.c b/usr.bin/xlint/lint1/msg_377.c
new file mode 100644
index 000000000000..0416ea6c1bb2
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_377.c
@@ -0,0 +1,41 @@
+/* $NetBSD: msg_377.c,v 1.5 2024/08/31 06:57:32 rillig Exp $ */
+# 3 "msg_377.c"
+
+// Test for message: redundant '\0' at the end of the format [377]
+
+/*
+ * Each conversion in the new-style format ends with a '\0' that needs to be
+ * spelled out.
+ *
+ * In both old-style and new-style formats, the '\0' that ends the whole
+ * format is provided by the compiler as part of the string literal.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+example(unsigned u32, uint64_t u64)
+{
+ char buf[64];
+
+ /* expect+7: warning: bit position '\000' (0) in '\000out-of-range' out of range 1..32 [371] */
+ /* expect+6: warning: redundant '\0' at the end of the format [377] */
+ snprintb(buf, sizeof(buf),
+ "\020"
+ "\005bit"
+ "\000out-of-range"
+ "\0",
+ u32);
+
+ /* expect+5: warning: redundant '\0' at the end of the format [377] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "b\005bit\0"
+ "\0",
+ u64);
+}
diff --git a/usr.bin/xlint/lint1/msg_378.c b/usr.bin/xlint/lint1/msg_378.c
new file mode 100644
index 000000000000..4698e99a9e9e
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_378.c
@@ -0,0 +1,52 @@
+/* $NetBSD: msg_378.c,v 1.3 2024/08/31 06:57:32 rillig Exp $ */
+# 3 "msg_378.c"
+
+// Test for message: conversion '%.*s' is unreachable by input value [378]
+
+/*
+ * The typical use case of snprintb is to have a format that is specifically
+ * tailored to a particular input value. Often, a format is only used in a
+ * single place. Therefore, bits that are unreachable are redundant and may
+ * hint at typos.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof(0)) size_t;
+typedef unsigned long long uint64_t;
+
+int snprintb(char *, size_t, const char *, uint64_t);
+
+void
+example(unsigned u32, uint64_t u64)
+{
+ char buf[64];
+
+ /* expect+5: warning: conversion '\040bit32' is unreachable by input value [378] */
+ snprintb(buf, sizeof(buf),
+ "\020"
+ "\037bit31"
+ "\040bit32",
+ u32 >> 1);
+
+ /* expect+5: warning: conversion 'b\075bit61\0' is unreachable by input value [378] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "b\074bit60\0"
+ "b\075bit61\0",
+ u64 >> 3);
+
+ /* expect+12: warning: conversion 'b\000bit0\0' is unreachable by input value [378] */
+ /* expect+11: warning: conversion 'b\011bit9\0' is unreachable by input value [378] */
+ /* expect+10: warning: conversion 'f\017\002bits15-16\0' is unreachable by input value [378] */
+ /* expect+9: warning: conversion 'f\050\030bits40-63\0' is unreachable by input value [378] */
+ snprintb(buf, sizeof(buf),
+ "\177\020"
+ "b\000bit0\0"
+ "b\010bit8\0"
+ "b\011bit9\0"
+ "f\012\002bits10-11\0"
+ "f\017\002bits15-16\0"
+ "f\050\030bits40-63\0",
+ (u32 & 0xaa55aa55) << 8);
+}
diff --git a/usr.bin/xlint/lint1/msg_379.c b/usr.bin/xlint/lint1/msg_379.c
new file mode 100644
index 000000000000..bb2b98917867
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_379.c
@@ -0,0 +1,31 @@
+/* $NetBSD: msg_379.c,v 1.2 2024/11/13 04:32:50 rillig Exp $ */
+# 3 "msg_379.c"
+
+// Test for message: comparing integer '%s' to floating point constant %Lg [379]
+
+/*
+ * Comparing an integer expression to a floating point constant mixes
+ * different kinds of types. This mixture is more complicated than necessary,
+ * thus confusing human readers.
+ *
+ * The compilers are fine with this kind of expression: GCC treats the
+ * constant as an integer even at -O0 while Clang needs at least -O.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+int
+comparisons(int x)
+{
+ if (3 > 123.0)
+ /* expect+1: warning: 'return' statement not reached [193] */
+ return 0;
+ /* expect+1: warning: comparing integer 'int' to floating point constant 123 [379] */
+ if (x > 123.0)
+ return 1;
+
+ // Yoda-style comparisons are unusual enough to not warn about them.
+ if (123.0 > x)
+ return 2;
+ return 3;
+}
diff --git a/usr.bin/xlint/lint1/msg_380.c b/usr.bin/xlint/lint1/msg_380.c
new file mode 100644
index 000000000000..b729380789a3
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_380.c
@@ -0,0 +1,71 @@
+/* $NetBSD: msg_380.c,v 1.2 2024/06/22 06:24:46 rillig Exp $ */
+# 3 "msg_380.c"
+
+// Test for message: lossy conversion of %Lg to '%s', arg #%d [380]
+
+/* lint1-extra-flags: -X 351 */
+
+void take_s32(int);
+void take_u32(unsigned int);
+void take_s64(long long);
+void take_u64(unsigned long long);
+
+void
+conversions(void)
+{
+ /* expect+1: warning: lossy conversion of -2.14748e+09 to 'int', arg #1 [380] */
+ take_s32(-2147483649.0);
+ take_s32(-2147483648.0);
+ /* expect+1: warning: lossy conversion of 3.141 to 'int', arg #1 [380] */
+ take_s32(3.141);
+ take_s32(2147483647.0);
+ /* expect+1: warning: lossy conversion of 2.14748e+09 to 'int', arg #1 [380] */
+ take_s32(2147483648.0);
+
+ /* expect+1: warning: lossy conversion of -1 to 'unsigned int', arg #1 [380] */
+ take_u32(-1.0);
+ take_u32(-0.0);
+ take_u32(0.0);
+ /* expect+1: warning: lossy conversion of 3.141 to 'unsigned int', arg #1 [380] */
+ take_u32(3.141);
+ take_u32(4294967295.0);
+ /* expect+1: warning: lossy conversion of 4.29497e+09 to 'unsigned int', arg #1 [380] */
+ take_u32(4294967296.0);
+
+ /* expect+1: warning: lossy conversion of -9.22337e+18 to 'long long', arg #1 [380] */
+ take_s64(-9223372036854776833.0);
+ /* The constant ...809 is rounded down to ...808, thus no warning. */
+ take_s64(-9223372036854775809.0);
+ take_s64(-9223372036854775808.0);
+ /* expect+1: warning: lossy conversion of 3.141 to 'long long', arg #1 [380] */
+ take_s64(3.141);
+ /* expect+1: warning: lossy conversion of 9.22337e+18 to 'long long', arg #1 [380] */
+ take_s64(9223372036854775807.0);
+ /* expect+1: warning: lossy conversion of 9.22337e+18 to 'long long', arg #1 [380] */
+ take_s64(9223372036854775808.0);
+
+ /* expect+1: warning: lossy conversion of -1 to 'unsigned long long', arg #1 [380] */
+ take_u64(-1.0);
+ take_u64(-0.0);
+ take_u64(0.0);
+ /* expect+1: warning: lossy conversion of 3.141 to 'unsigned long long', arg #1 [380] */
+ take_u64(3.141);
+
+ // Warning on: alpha
+ // No warning on: aarch64 aarch64-compat32 arm i386 mips powerpc riscv64 sh3 sparc x86_64
+ // Unknown: coldfire hppa ia64 m68000 m68k mips64 mipsn64 or1k powerpc64 riscv32 sparc64 vax
+ //
+ // warning: lossy conversion of 1.84467e+19 to 'unsigned long long', arg #1 [380]
+ //take_u64(18446744073709550591.0);
+
+ // Warning on: aarch64 alpha arm i386 mips riscv64 sparc x86_64
+ // No warning on: aarch64-compat32 powerpc sh3
+ // Unknown: coldfire hppa ia64 m68000 m68k mips64 mipsn64 or1k powerpc64 riscv32 sparc64 vax
+ //
+ // warning: lossy conversion of 1.84467e+19 to 'unsigned long long', arg #1 [380]
+ //take_u64(18446744073709550592.0);
+ // warning: lossy conversion of 1.84467e+19 to 'unsigned long long', arg #1 [380]
+ //take_u64(18446744073709551615.0);
+ // warning: lossy conversion of 1.84467e+19 to 'unsigned long long', arg #1 [380]
+ //take_u64(18446744073709551616.0);
+}
diff --git a/usr.bin/xlint/lint1/msg_381.c b/usr.bin/xlint/lint1/msg_381.c
new file mode 100644
index 000000000000..4bf64e3cf895
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_381.c
@@ -0,0 +1,71 @@
+/* $NetBSD: msg_381.c,v 1.2 2024/06/22 06:24:46 rillig Exp $ */
+# 3 "msg_381.c"
+
+// Test for message: lossy conversion of %Lg to '%s' [381]
+
+/* lint1-extra-flags: -X 351 */
+
+int s32;
+unsigned int u32;
+long long s64;
+unsigned long long u64;
+
+void
+conversions(void)
+{
+ /* expect+1: warning: lossy conversion of -2.14748e+09 to 'int' [381] */
+ s32 = -2147483649.0;
+ s32 = -2147483648.0;
+ /* expect+1: warning: lossy conversion of 3.141 to 'int' [381] */
+ s32 = 3.141;
+ s32 = 2147483647.0;
+ /* expect+1: warning: lossy conversion of 2.14748e+09 to 'int' [381] */
+ s32 = 2147483648.0;
+
+ /* expect+1: warning: lossy conversion of -1 to 'unsigned int' [381] */
+ u32 = -1.0;
+ u32 = -0.0;
+ u32 = 0.0;
+ /* expect+1: warning: lossy conversion of 3.141 to 'unsigned int' [381] */
+ u32 = 3.141;
+ u32 = 4294967295.0;
+ /* expect+1: warning: lossy conversion of 4.29497e+09 to 'unsigned int' [381] */
+ u32 = 4294967296.0;
+
+ /* expect+1: warning: lossy conversion of -9.22337e+18 to 'long long' [381] */
+ s64 = -9223372036854776833.0;
+ /* The constant ...809 is rounded down to ...808, thus no warning. */
+ s64 = -9223372036854775809.0;
+ s64 = -9223372036854775808.0;
+ /* expect+1: warning: lossy conversion of 3.141 to 'long long' [381] */
+ s64 = 3.141;
+ /* expect+1: warning: lossy conversion of 9.22337e+18 to 'long long' [381] */
+ s64 = 9223372036854775807.0;
+ /* expect+1: warning: lossy conversion of 9.22337e+18 to 'long long' [381] */
+ s64 = 9223372036854775808.0;
+
+ /* expect+1: warning: lossy conversion of -1 to 'unsigned long long' [381] */
+ u64 = -1.0;
+ u64 = -0.0;
+ u64 = 0.0;
+ /* expect+1: warning: lossy conversion of 3.141 to 'unsigned long long' [381] */
+ u64 = 3.141;
+
+ // Warning on: alpha
+ // No warning on: aarch64 aarch64-compat32 arm i386 mips powerpc riscv64 sh3 sparc x86_64
+ // Unknown: coldfire hppa ia64 m68000 m68k mips64 mipsn64 or1k powerpc64 riscv32 sparc64 vax
+ //
+ // warning: lossy conversion of 1.84467e+19 to 'unsigned long long' [381]
+ //u64 = 18446744073709550591.0;
+
+ // Warning on: aarch64 alpha arm i386 mips riscv64 sparc x86_64
+ // No warning on: aarch64-compat32 powerpc sh3
+ // Unknown: coldfire hppa ia64 m68000 m68k mips64 mipsn64 or1k powerpc64 riscv32 sparc64 vax
+ //
+ // warning: lossy conversion of 1.84467e+19 to 'unsigned long long' [381]
+ //u64 = 18446744073709550592.0;
+ // warning: lossy conversion of 1.84467e+19 to 'unsigned long long' [381]
+ //u64 = 18446744073709551615.0;
+ // warning: lossy conversion of 1.84467e+19 to 'unsigned long long' [381]
+ //u64 = 18446744073709551616.0;
+}
diff --git a/usr.bin/xlint/lint1/msg_382.c b/usr.bin/xlint/lint1/msg_382.c
new file mode 100644
index 000000000000..6b72ce0c4071
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_382.c
@@ -0,0 +1,42 @@
+/* $NetBSD: msg_382.c,v 1.2 2025/04/12 17:22:50 rillig Exp $ */
+# 3 "msg_382.c"
+
+// Test for message: constant assignment of type '%s' in operand of '%s' always evaluates to '%s' [382]
+
+/*
+ * Outside strict bool mode, an assignment can be used as a condition, but
+ * that is generally wrong. Especially if a constant is assigned to a
+ * variable, the condition always evaluates to that constant value, which
+ * indicates a typo, as '==' makes more sense than '=' in a condition.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+int
+conversions(int a, int b)
+{
+ /* expect+1: warning: constant assignment of type 'int' in operand of '!' always evaluates to 'true' [382] */
+ if (!(a = 13))
+ return 1;
+ /* expect+1: warning: constant assignment of type 'int' in operand of '!' always evaluates to 'false' [382] */
+ if (!(b = 0))
+ return 2;
+ if (!(a = a + 1))
+ return 3;
+
+ /* expect+1: warning: constant assignment of type 'int' in operand of '&&' always evaluates to 'true' [382] */
+ if ((a = 13) && (b == 14))
+ return 4;
+ /* expect+1: warning: constant assignment of type 'int' in operand of '&&' always evaluates to 'true' [382] */
+ if ((a == 13) && (b = 14))
+ return 5;
+
+ /* expect+1: warning: constant assignment of type 'int' in operand of '||' always evaluates to 'true' [382] */
+ if ((a = 13) || (b == 14))
+ return 4;
+ /* expect+1: warning: constant assignment of type 'int' in operand of '||' always evaluates to 'true' [382] */
+ if ((a == 13) || (b = 14))
+ return 5;
+
+ return a;
+}
diff --git a/usr.bin/xlint/lint1/msg_383.c b/usr.bin/xlint/lint1/msg_383.c
new file mode 100644
index 000000000000..7bfee2d6d34d
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_383.c
@@ -0,0 +1,56 @@
+/* $NetBSD: msg_383.c,v 1.4 2025/05/04 09:40:03 rillig Exp $ */
+# 3 "msg_383.c"
+
+// Test for message: passing '%s' as argument %d to '%s' discards '%s' [383]
+
+/* lint1-extra-flags: -X 351 */
+
+void sink_char(char *, const char *, volatile char *, const volatile char *);
+void sink_int(int *, const int *, volatile int *, const volatile int *);
+
+void (*indirect_char)(char *, const char *, volatile char *, const volatile char *);
+
+struct {
+ void (*member_char)(char *, const char *, volatile char *, const volatile char *);
+} doubly_indirect;
+
+void
+caller(const volatile char *cvcp, const volatile int *cvip, int (*fn)(void))
+{
+ /* expect+3: warning: passing 'pointer to const volatile char' as argument 1 to 'sink_char' discards 'const volatile' [383] */
+ /* expect+2: warning: passing 'pointer to const volatile char' as argument 2 to 'sink_char' discards 'volatile' [383] */
+ /* expect+1: warning: passing 'pointer to const volatile char' as argument 3 to 'sink_char' discards 'const' [383] */
+ sink_char(cvcp, cvcp, cvcp, cvcp);
+ /* expect+3: warning: passing 'pointer to const volatile int' as argument 1 to 'sink_int' discards 'const volatile' [383] */
+ /* expect+2: warning: passing 'pointer to const volatile int' as argument 2 to 'sink_int' discards 'volatile' [383] */
+ /* expect+1: warning: passing 'pointer to const volatile int' as argument 3 to 'sink_int' discards 'const' [383] */
+ sink_int(cvip, cvip, cvip, cvip);
+ /* expect+4: warning: converting 'pointer to function(void) returning int' to incompatible 'pointer to char' for argument 1 [153] */
+ /* expect+3: warning: converting 'pointer to function(void) returning int' to incompatible 'pointer to const char' for argument 2 [153] */
+ /* expect+2: warning: converting 'pointer to function(void) returning int' to incompatible 'pointer to volatile char' for argument 3 [153] */
+ /* expect+1: warning: converting 'pointer to function(void) returning int' to incompatible 'pointer to const volatile char' for argument 4 [153] */
+ sink_char(fn, fn, fn, fn);
+
+ /* expect+3: warning: passing 'pointer to const volatile char' as argument 1 to 'indirect_char' discards 'const volatile' [383] */
+ /* expect+2: warning: passing 'pointer to const volatile char' as argument 2 to 'indirect_char' discards 'volatile' [383] */
+ /* expect+1: warning: passing 'pointer to const volatile char' as argument 3 to 'indirect_char' discards 'const' [383] */
+ indirect_char(cvcp, cvcp, cvcp, cvcp);
+
+ /* expect+3: warning: passing 'pointer to const volatile char' as argument 1 to 'function(pointer to char, pointer to const char, pointer to volatile char, pointer to const volatile char) returning void' discards 'const volatile' [383] */
+ /* expect+2: warning: passing 'pointer to const volatile char' as argument 2 to 'function(pointer to char, pointer to const char, pointer to volatile char, pointer to const volatile char) returning void' discards 'volatile' [383] */
+ /* expect+1: warning: passing 'pointer to const volatile char' as argument 3 to 'function(pointer to char, pointer to const char, pointer to volatile char, pointer to const volatile char) returning void' discards 'const' [383] */
+ doubly_indirect.member_char(cvcp, cvcp, cvcp, cvcp);
+}
+
+
+typedef int array[8];
+typedef const int *pointer_to_const_array;
+
+// The 'const' applies to the pointer target, making it 'const int *'.
+int const_array_callee(const array);
+
+static inline int
+const_array_caller(pointer_to_const_array ptr)
+{
+ return const_array_callee(ptr);
+}
diff --git a/usr.bin/xlint/lint1/msg_384.c b/usr.bin/xlint/lint1/msg_384.c
new file mode 100644
index 000000000000..9c7d21377c26
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_384.c
@@ -0,0 +1,26 @@
+/* $NetBSD: msg_384.c,v 1.2 2025/01/03 03:14:47 rillig Exp $ */
+# 3 "msg_384.c"
+
+// Test for message: function definition for '%s' with identifier list is obsolete in C23 [384]
+
+/* lint1-extra-flags: -X 351 */
+
+/*
+ * In traditional C, defining a function by listing its parameter names first,
+ * followed by declarations, was usual. This practice has been obsoleted in
+ * favor of defining the parameter types right in the declarator.
+ */
+
+static inline int
+/* expect+1: warning: function definition for 'function_with_identifier_list' with identifier list is obsolete in C23 [384] */
+function_with_identifier_list(a, b)
+ int a, b;
+{
+ return a + b;
+}
+
+static inline int
+function_with_prototype(int a, int b)
+{
+ return a + b;
+}
diff --git a/usr.bin/xlint/lint1/msg_385.c b/usr.bin/xlint/lint1/msg_385.c
new file mode 100644
index 000000000000..fdc749066e06
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_385.c
@@ -0,0 +1,58 @@
+/* $NetBSD: msg_385.c,v 1.2 2025/03/10 22:08:36 rillig Exp $ */
+# 3 "msg_385.c"
+
+// Test for message: do-while macro '%.*s' ends with semicolon [385]
+
+/*
+ * A function-like macro that consists of a do-while statement is intended to
+ * expand to a single statement, but without the trailing semicolon, as the
+ * semicolon is already provided by the calling site. When the macro expansion
+ * ends with a semicolon, there are two semicolons, which can lead to syntax
+ * errors.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+/* expect+1: warning: do-while macro 'wrong_stmt' ends with semicolon [385] */
+#define wrong_stmt() do { } while (0);
+
+#define correct_stmt() do { } while (0)
+
+/* expect+5: warning: do-while macro 'wrong_stmt_with_comment' ends with semicolon [385] */
+#define wrong_stmt_with_comment() do { } while (0); /*
+a
+b
+c
+*/
+
+#define correct_stmt_with_comment() do { } while (0) /*
+a
+b
+c
+*/
+
+/* The comment marker inside the string literal does not start a comment. */
+#define stmt_with_string() do { print("/*"); } while (0)
+
+void
+call_wrong_stmt(int x)
+{
+ if (x > 0)
+ do { } while (0);;
+ /* expect+1: error: syntax error 'else' [249] */
+ else
+ do { } while (0);;
+}
+
+void
+call_correct_stmt(int x)
+{
+ if (x < 0)
+ do { } while (0);
+ else
+ do { } while (0);
+}
+
+// The macro expansion does start with "do", but not with the keyword "do",
+// so don't warn in this case.
+#define unrelated() do_something();
diff --git a/usr.bin/xlint/lint1/msg_386.c b/usr.bin/xlint/lint1/msg_386.c
new file mode 100644
index 000000000000..fd46580be6c7
--- /dev/null
+++ b/usr.bin/xlint/lint1/msg_386.c
@@ -0,0 +1,34 @@
+/* $NetBSD: msg_386.c,v 1.1 2025/08/31 20:43:27 rillig Exp $ */
+# 3 "msg_386.c"
+
+// Test for message: conversion '%.*s' does not mix with '%c' [386]
+
+/*
+ * In the snprintb format string, the conversions 'f' and '=' mix well, and so
+ * do 'F' and ':'. But 'f' doesn't mix with ':', and neither does 'F' mix with
+ * '='.
+ */
+
+/* lint1-extra-flags: -X 351 */
+
+typedef typeof(sizeof 0) size_t;
+
+void snprintb(char *, size_t, const char *, unsigned long long);
+
+void
+test_snprintb(void)
+{
+ char buf[50];
+
+ snprintb(buf, sizeof buf,
+ "\177\020"
+ "f\000\020" "field\0"
+ "" "=\000" "mix\0"
+ "" ":\001" "no-mix\0"
+ "F\020\020" "field\0"
+ "" "=\000" "no-mix\0"
+ "" ":\000" "mix\0",
+ /* expect+2: warning: conversion ':' does not mix with 'f' [386] */
+ /* expect+1: warning: conversion '=' does not mix with 'F' [386] */
+ 0xffffffff);
+}
diff --git a/usr.bin/xlint/lint1/platform_ilp32_c90.c b/usr.bin/xlint/lint1/platform_ilp32_c90.c
new file mode 100644
index 000000000000..73702b2c3f58
--- /dev/null
+++ b/usr.bin/xlint/lint1/platform_ilp32_c90.c
@@ -0,0 +1,243 @@
+/* $NetBSD: platform_ilp32_c90.c,v 1.3 2024/01/28 08:26:07 rillig Exp $ */
+# 3 "platform_ilp32_c90.c"
+
+/*
+ * Tests that are specific to ILP32 platforms and the language level C90.
+ */
+
+/* lint1-flags: -sw -X 351 */
+/* lint1-only-if: ilp32 */
+
+void *lex_integer[] = {
+ /* expect+1: ... integer 'int' ... */
+ 2147483647,
+ /* expect+1: ... integer 'int' ... */
+ 0x7fffffff,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 2147483648,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0x80000000,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 4294967295,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0xffffffff,
+ /* expect+1: warning: integer constant out of range [252] */
+ 4294967296,
+ /* expect+1: warning: integer constant out of range [252] */
+ 0x0000000100000000,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 9223372036854775807,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x7fffffffffffffff,
+ /* expect+1: warning: integer constant out of range [252] */
+ 9223372036854775808,
+ /* expect+1: warning: integer constant out of range [252] */
+ 0x8000000000000000,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551615,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0xffffffffffffffff,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551616,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x00010000000000000000,
+
+ /* expect+1: ... integer 'unsigned int' ... */
+ 2147483647U,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0x7fffffffU,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 2147483648U,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0x80000000U,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 4294967295U,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0xffffffffU,
+ /* expect+1: warning: integer constant out of range [252] */
+ 4294967296U,
+ /* expect+1: warning: integer constant out of range [252] */
+ 0x0000000100000000U,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 9223372036854775807U,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x7fffffffffffffffU,
+ /* expect+1: warning: integer constant out of range [252] */
+ 9223372036854775808U,
+ /* expect+1: warning: integer constant out of range [252] */
+ 0x8000000000000000U,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551615U,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0xffffffffffffffffU,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551616U,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x00010000000000000000U,
+
+ /* expect+1: ... integer 'long' ... */
+ 2147483647L,
+ /* expect+1: ... integer 'long' ... */
+ 0x7fffffffL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 2147483648L,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x80000000L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 4294967295L,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0xffffffffL,
+ /* expect+1: warning: integer constant out of range [252] */
+ 4294967296L,
+ /* expect+1: warning: integer constant out of range [252] */
+ 0x0000000100000000L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 9223372036854775807L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x7fffffffffffffffL,
+ /* expect+1: warning: integer constant out of range [252] */
+ 9223372036854775808L,
+ /* expect+1: warning: integer constant out of range [252] */
+ 0x8000000000000000L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551615L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0xffffffffffffffffL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551616L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x00010000000000000000L,
+
+ /* expect+1: ... integer 'unsigned long' ... */
+ 2147483647UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x7fffffffUL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 2147483648UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x80000000UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 4294967295UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0xffffffffUL,
+ /* expect+1: warning: integer constant out of range [252] */
+ 4294967296UL,
+ /* expect+1: warning: integer constant out of range [252] */
+ 0x0000000100000000UL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 9223372036854775807UL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x7fffffffffffffffUL,
+ /* expect+1: warning: integer constant out of range [252] */
+ 9223372036854775808UL,
+ /* expect+1: warning: integer constant out of range [252] */
+ 0x8000000000000000UL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551615UL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0xffffffffffffffffUL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551616UL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x00010000000000000000UL,
+
+ /* expect+1: ... integer 'long long' ... */
+ 2147483647LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x7fffffffLL,
+ /* expect+1: ... integer 'long long' ... */
+ 2147483648LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x80000000LL,
+ /* expect+1: ... integer 'long long' ... */
+ 4294967295LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0xffffffffLL,
+ /* expect+1: ... integer 'long long' ... */
+ 4294967296LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x0000000100000000LL,
+ /* expect+1: ... integer 'long long' ... */
+ 9223372036854775807LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x7fffffffffffffffLL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775808LL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x8000000000000000LL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551615LL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0xffffffffffffffffLL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551616LL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x00010000000000000000LL,
+
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 2147483647ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x7fffffffULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 2147483648ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x80000000ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 4294967295ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0xffffffffULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 4294967296ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x0000000100000000ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775807ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x7fffffffffffffffULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775808ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x8000000000000000ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551615ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0xffffffffffffffffULL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551616ULL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x00010000000000000000ULL,
+};
diff --git a/usr.bin/xlint/lint1/platform_ilp32_c99.c b/usr.bin/xlint/lint1/platform_ilp32_c99.c
new file mode 100644
index 000000000000..94126a816eb2
--- /dev/null
+++ b/usr.bin/xlint/lint1/platform_ilp32_c99.c
@@ -0,0 +1,227 @@
+/* $NetBSD: platform_ilp32_c99.c,v 1.3 2024/01/28 08:26:07 rillig Exp $ */
+# 3 "platform_ilp32_c99.c"
+
+/*
+ * Tests that are specific to ILP32 platforms and the language level C99.
+ */
+
+/* lint1-flags: -Sw -X 351 */
+/* lint1-only-if: ilp32 */
+
+void *lex_integer[] = {
+ /* expect+1: ... integer 'int' ... */
+ 2147483647,
+ /* expect+1: ... integer 'int' ... */
+ 0x7fffffff,
+ /* expect+1: ... integer 'long long' ... */
+ 2147483648,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0x80000000,
+ /* expect+1: ... integer 'long long' ... */
+ 4294967295,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0xffffffff,
+ /* expect+1: ... integer 'long long' ... */
+ 4294967296,
+ /* expect+1: ... integer 'long long' ... */
+ 0x0000000100000000,
+ /* expect+1: ... integer 'long long' ... */
+ 9223372036854775807,
+ /* expect+1: ... integer 'long long' ... */
+ 0x7fffffffffffffff,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775808,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x8000000000000000,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551615,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0xffffffffffffffff,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551616,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x00010000000000000000,
+
+ /* expect+1: ... integer 'unsigned int' ... */
+ 2147483647U,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0x7fffffffU,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 2147483648U,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0x80000000U,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 4294967295U,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0xffffffffU,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 4294967296U,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x0000000100000000U,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775807U,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x7fffffffffffffffU,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775808U,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x8000000000000000U,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551615U,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0xffffffffffffffffU,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551616U,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x00010000000000000000U,
+
+ /* expect+1: ... integer 'long' ... */
+ 2147483647L,
+ /* expect+1: ... integer 'long' ... */
+ 0x7fffffffL,
+ /* expect+1: ... integer 'long long' ... */
+ 2147483648L,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x80000000L,
+ /* expect+1: ... integer 'long long' ... */
+ 4294967295L,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0xffffffffL,
+ /* expect+1: ... integer 'long long' ... */
+ 4294967296L,
+ /* expect+1: ... integer 'long long' ... */
+ 0x0000000100000000L,
+ /* expect+1: ... integer 'long long' ... */
+ 9223372036854775807L,
+ /* expect+1: ... integer 'long long' ... */
+ 0x7fffffffffffffffL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775808L,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x8000000000000000L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551615L,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0xffffffffffffffffL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551616L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x00010000000000000000L,
+
+ /* expect+1: ... integer 'unsigned long' ... */
+ 2147483647UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x7fffffffUL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 2147483648UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x80000000UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 4294967295UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0xffffffffUL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 4294967296UL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x0000000100000000UL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775807UL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x7fffffffffffffffUL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775808UL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x8000000000000000UL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551615UL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0xffffffffffffffffUL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551616UL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x00010000000000000000UL,
+
+ /* expect+1: ... integer 'long long' ... */
+ 2147483647LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x7fffffffLL,
+ /* expect+1: ... integer 'long long' ... */
+ 2147483648LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x80000000LL,
+ /* expect+1: ... integer 'long long' ... */
+ 4294967295LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0xffffffffLL,
+ /* expect+1: ... integer 'long long' ... */
+ 4294967296LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x0000000100000000LL,
+ /* expect+1: ... integer 'long long' ... */
+ 9223372036854775807LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x7fffffffffffffffLL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775808LL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x8000000000000000LL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551615LL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0xffffffffffffffffLL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551616LL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x00010000000000000000LL,
+
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 2147483647ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x7fffffffULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 2147483648ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x80000000ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 4294967295ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0xffffffffULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 4294967296ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x0000000100000000ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775807ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x7fffffffffffffffULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775808ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x8000000000000000ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551615ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0xffffffffffffffffULL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551616ULL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x00010000000000000000ULL,
+};
diff --git a/usr.bin/xlint/lint1/platform_ilp32_trad.c b/usr.bin/xlint/lint1/platform_ilp32_trad.c
new file mode 100644
index 000000000000..a8cc6fdafdc5
--- /dev/null
+++ b/usr.bin/xlint/lint1/platform_ilp32_trad.c
@@ -0,0 +1,130 @@
+/* $NetBSD: platform_ilp32_trad.c,v 1.4 2024/02/07 22:59:28 rillig Exp $ */
+# 3 "platform_ilp32_trad.c"
+
+/*
+ * Tests that are specific to ILP32 platforms and traditional C.
+ */
+
+/* lint1-flags: -tw -X 351 */
+/* lint1-only-if: ilp32 */
+
+void *lex_integer[] = {
+ /* expect+1: ... integer 'int' ... */
+ 2147483647,
+ /* expect+1: ... integer 'int' ... */
+ 0x7fffffff,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 2147483648,
+ /* expect+1: ... integer 'long' ... */
+ 0x80000000,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 4294967295,
+ /* expect+1: ... integer 'long' ... */
+ 0xffffffff,
+ /* expect+1: warning: integer constant out of range [252] */
+ 4294967296,
+ /* expect+1: warning: integer constant out of range [252] */
+ 0x0000000100000000,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 9223372036854775807,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 0x7fffffffffffffff,
+ /* expect+1: warning: integer constant out of range [252] */
+ 9223372036854775808,
+ /* expect+1: warning: integer constant out of range [252] */
+ 0x8000000000000000,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 18446744073709551615,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 0xffffffffffffffff,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 18446744073709551616,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 0x00010000000000000000,
+
+ /* expect+1: ... integer 'long' ... */
+ 2147483647L,
+ /* expect+1: ... integer 'long' ... */
+ 0x7fffffffL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 2147483648L,
+ /* expect+1: ... integer 'long' ... */
+ 0x80000000L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 4294967295L,
+ /* expect+1: ... integer 'long' ... */
+ 0xffffffffL,
+ /* expect+1: warning: integer constant out of range [252] */
+ 4294967296L,
+ /* expect+1: warning: integer constant out of range [252] */
+ 0x0000000100000000L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 9223372036854775807L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 0x7fffffffffffffffL,
+ /* expect+1: warning: integer constant out of range [252] */
+ 9223372036854775808L,
+ /* expect+1: warning: integer constant out of range [252] */
+ 0x8000000000000000L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 18446744073709551615L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 0xffffffffffffffffL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 18446744073709551616L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 0x00010000000000000000L,
+
+ /* expect+1: ... integer 'long long' ... */
+ 2147483647LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x7fffffffLL,
+ /* expect+1: ... integer 'long long' ... */
+ 2147483648LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x80000000LL,
+ /* expect+1: ... integer 'long long' ... */
+ 4294967295LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0xffffffffLL,
+ /* expect+1: ... integer 'long long' ... */
+ 4294967296LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x0000000100000000LL,
+ /* expect+1: ... integer 'long long' ... */
+ 9223372036854775807LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x7fffffffffffffffLL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long long' ... */
+ 9223372036854775808LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x8000000000000000LL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long long' ... */
+ 18446744073709551615LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0xffffffffffffffffLL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long long' ... */
+ 18446744073709551616LL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long long' ... */
+ 0x00010000000000000000LL,
+};
diff --git a/usr.bin/xlint/lint1/platform_lp64_c90.c b/usr.bin/xlint/lint1/platform_lp64_c90.c
new file mode 100644
index 000000000000..27868ccd8241
--- /dev/null
+++ b/usr.bin/xlint/lint1/platform_lp64_c90.c
@@ -0,0 +1,227 @@
+/* $NetBSD: platform_lp64_c90.c,v 1.3 2024/01/28 08:17:27 rillig Exp $ */
+# 3 "platform_lp64_c90.c"
+
+/*
+ * Tests that are specific to LP64 platforms and the language level C90.
+ */
+
+/* lint1-flags: -sw -X 351 */
+/* lint1-only-if: lp64 */
+
+void *lex_integer[] = {
+ /* expect+1: ... integer 'int' ... */
+ 2147483647,
+ /* expect+1: ... integer 'int' ... */
+ 0x7fffffff,
+ /* expect+1: ... integer 'long' ... */
+ 2147483648,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0x80000000,
+ /* expect+1: ... integer 'long' ... */
+ 4294967295,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0xffffffff,
+ /* expect+1: ... integer 'long' ... */
+ 4294967296,
+ /* expect+1: ... integer 'long' ... */
+ 0x0000000100000000,
+ /* expect+1: ... integer 'long' ... */
+ 9223372036854775807,
+ /* expect+1: ... integer 'long' ... */
+ 0x7fffffffffffffff,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 9223372036854775808,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x8000000000000000,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551615,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0xffffffffffffffff,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551616,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x00010000000000000000,
+
+ /* expect+1: ... integer 'unsigned int' ... */
+ 2147483647U,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0x7fffffffU,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 2147483648U,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0x80000000U,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 4294967295U,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0xffffffffU,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 4294967296U,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x0000000100000000U,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 9223372036854775807U,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x7fffffffffffffffU,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 9223372036854775808U,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x8000000000000000U,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551615U,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0xffffffffffffffffU,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551616U,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x00010000000000000000U,
+
+ /* expect+1: ... integer 'long' ... */
+ 2147483647L,
+ /* expect+1: ... integer 'long' ... */
+ 0x7fffffffL,
+ /* expect+1: ... integer 'long' ... */
+ 2147483648L,
+ /* expect+1: ... integer 'long' ... */
+ 0x80000000L,
+ /* expect+1: ... integer 'long' ... */
+ 4294967295L,
+ /* expect+1: ... integer 'long' ... */
+ 0xffffffffL,
+ /* expect+1: ... integer 'long' ... */
+ 4294967296L,
+ /* expect+1: ... integer 'long' ... */
+ 0x0000000100000000L,
+ /* expect+1: ... integer 'long' ... */
+ 9223372036854775807L,
+ /* expect+1: ... integer 'long' ... */
+ 0x7fffffffffffffffL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 9223372036854775808L,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x8000000000000000L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551615L,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0xffffffffffffffffL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551616L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x00010000000000000000L,
+
+ /* expect+1: ... integer 'unsigned long' ... */
+ 2147483647UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x7fffffffUL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 2147483648UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x80000000UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 4294967295UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0xffffffffUL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 4294967296UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x0000000100000000UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 9223372036854775807UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x7fffffffffffffffUL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 9223372036854775808UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x8000000000000000UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551615UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0xffffffffffffffffUL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551616UL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x00010000000000000000UL,
+
+ /* expect+1: ... integer 'long long' ... */
+ 2147483647LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x7fffffffLL,
+ /* expect+1: ... integer 'long long' ... */
+ 2147483648LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x80000000LL,
+ /* expect+1: ... integer 'long long' ... */
+ 4294967295LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0xffffffffLL,
+ /* expect+1: ... integer 'long long' ... */
+ 4294967296LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x0000000100000000LL,
+ /* expect+1: ... integer 'long long' ... */
+ 9223372036854775807LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x7fffffffffffffffLL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775808LL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x8000000000000000LL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551615LL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0xffffffffffffffffLL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551616LL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x00010000000000000000LL,
+
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 2147483647ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x7fffffffULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 2147483648ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x80000000ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 4294967295ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0xffffffffULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 4294967296ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x0000000100000000ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775807ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x7fffffffffffffffULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775808ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x8000000000000000ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551615ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0xffffffffffffffffULL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551616ULL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x00010000000000000000ULL,
+};
diff --git a/usr.bin/xlint/lint1/platform_lp64_c99.c b/usr.bin/xlint/lint1/platform_lp64_c99.c
new file mode 100644
index 000000000000..007ad2a020c7
--- /dev/null
+++ b/usr.bin/xlint/lint1/platform_lp64_c99.c
@@ -0,0 +1,227 @@
+/* $NetBSD: platform_lp64_c99.c,v 1.3 2024/01/28 08:17:27 rillig Exp $ */
+# 3 "platform_lp64_c99.c"
+
+/*
+ * Tests that are specific to LP64 platforms and the language level C99.
+ */
+
+/* lint1-flags: -Sw -X 351 */
+/* lint1-only-if: lp64 */
+
+void *lex_integer[] = {
+ /* expect+1: ... integer 'int' ... */
+ 2147483647,
+ /* expect+1: ... integer 'int' ... */
+ 0x7fffffff,
+ /* expect+1: ... integer 'long' ... */
+ 2147483648,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0x80000000,
+ /* expect+1: ... integer 'long' ... */
+ 4294967295,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0xffffffff,
+ /* expect+1: ... integer 'long' ... */
+ 4294967296,
+ /* expect+1: ... integer 'long' ... */
+ 0x0000000100000000,
+ /* expect+1: ... integer 'long' ... */
+ 9223372036854775807,
+ /* expect+1: ... integer 'long' ... */
+ 0x7fffffffffffffff,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775808,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x8000000000000000,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551615,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0xffffffffffffffff,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551616,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x00010000000000000000,
+
+ /* expect+1: ... integer 'unsigned int' ... */
+ 2147483647U,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0x7fffffffU,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 2147483648U,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0x80000000U,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 4294967295U,
+ /* expect+1: ... integer 'unsigned int' ... */
+ 0xffffffffU,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 4294967296U,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x0000000100000000U,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 9223372036854775807U,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x7fffffffffffffffU,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 9223372036854775808U,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x8000000000000000U,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551615U,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0xffffffffffffffffU,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551616U,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x00010000000000000000U,
+
+ /* expect+1: ... integer 'long' ... */
+ 2147483647L,
+ /* expect+1: ... integer 'long' ... */
+ 0x7fffffffL,
+ /* expect+1: ... integer 'long' ... */
+ 2147483648L,
+ /* expect+1: ... integer 'long' ... */
+ 0x80000000L,
+ /* expect+1: ... integer 'long' ... */
+ 4294967295L,
+ /* expect+1: ... integer 'long' ... */
+ 0xffffffffL,
+ /* expect+1: ... integer 'long' ... */
+ 4294967296L,
+ /* expect+1: ... integer 'long' ... */
+ 0x0000000100000000L,
+ /* expect+1: ... integer 'long' ... */
+ 9223372036854775807L,
+ /* expect+1: ... integer 'long' ... */
+ 0x7fffffffffffffffL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775808L,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x8000000000000000L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551615L,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0xffffffffffffffffL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551616L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x00010000000000000000L,
+
+ /* expect+1: ... integer 'unsigned long' ... */
+ 2147483647UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x7fffffffUL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 2147483648UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x80000000UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 4294967295UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0xffffffffUL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 4294967296UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x0000000100000000UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 9223372036854775807UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x7fffffffffffffffUL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 9223372036854775808UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x8000000000000000UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551615UL,
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0xffffffffffffffffUL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 18446744073709551616UL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long' ... */
+ 0x00010000000000000000UL,
+
+ /* expect+1: ... integer 'long long' ... */
+ 2147483647LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x7fffffffLL,
+ /* expect+1: ... integer 'long long' ... */
+ 2147483648LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x80000000LL,
+ /* expect+1: ... integer 'long long' ... */
+ 4294967295LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0xffffffffLL,
+ /* expect+1: ... integer 'long long' ... */
+ 4294967296LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x0000000100000000LL,
+ /* expect+1: ... integer 'long long' ... */
+ 9223372036854775807LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x7fffffffffffffffLL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775808LL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x8000000000000000LL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551615LL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0xffffffffffffffffLL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551616LL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x00010000000000000000LL,
+
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 2147483647ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x7fffffffULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 2147483648ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x80000000ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 4294967295ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0xffffffffULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 4294967296ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x0000000100000000ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775807ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x7fffffffffffffffULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 9223372036854775808ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x8000000000000000ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551615ULL,
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0xffffffffffffffffULL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 18446744073709551616ULL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'unsigned long long' ... */
+ 0x00010000000000000000ULL,
+};
diff --git a/usr.bin/xlint/lint1/platform_lp64_trad.c b/usr.bin/xlint/lint1/platform_lp64_trad.c
new file mode 100644
index 000000000000..a57db68ceb2e
--- /dev/null
+++ b/usr.bin/xlint/lint1/platform_lp64_trad.c
@@ -0,0 +1,122 @@
+/* $NetBSD: platform_lp64_trad.c,v 1.4 2024/02/07 07:42:50 rillig Exp $ */
+# 3 "platform_lp64_trad.c"
+
+/*
+ * Tests that are specific to LP64 platforms and traditional C.
+ */
+
+/* lint1-flags: -tw -X 351 */
+/* lint1-only-if: lp64 */
+
+void *lex_integer[] = {
+ /* expect+1: ... integer 'int' ... */
+ 2147483647,
+ /* expect+1: ... integer 'int' ... */
+ 0x7fffffff,
+ /* expect+1: ... integer 'long' ... */
+ 2147483648,
+ /* expect+1: ... integer 'long' ... */
+ 0x80000000,
+ /* expect+1: ... integer 'long' ... */
+ 4294967295,
+ /* expect+1: ... integer 'long' ... */
+ 0xffffffff,
+ /* expect+1: ... integer 'long' ... */
+ 4294967296,
+ /* expect+1: ... integer 'long' ... */
+ 0x0000000100000000,
+ /* expect+1: ... integer 'long' ... */
+ 9223372036854775807,
+ /* expect+1: ... integer 'long' ... */
+ 0x7fffffffffffffff,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 9223372036854775808,
+ /* expect+1: ... integer 'long' ... */
+ 0x8000000000000000,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 18446744073709551615,
+ /* expect+1: ... integer 'long' ... */
+ 0xffffffffffffffff,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 18446744073709551616,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 0x00010000000000000000,
+
+ /* expect+1: ... integer 'long' ... */
+ 2147483647L,
+ /* expect+1: ... integer 'long' ... */
+ 0x7fffffffL,
+ /* expect+1: ... integer 'long' ... */
+ 2147483648L,
+ /* expect+1: ... integer 'long' ... */
+ 0x80000000L,
+ /* expect+1: ... integer 'long' ... */
+ 4294967295L,
+ /* expect+1: ... integer 'long' ... */
+ 0xffffffffL,
+ /* expect+1: ... integer 'long' ... */
+ 4294967296L,
+ /* expect+1: ... integer 'long' ... */
+ 0x0000000100000000L,
+ /* expect+1: ... integer 'long' ... */
+ 9223372036854775807L,
+ /* expect+1: ... integer 'long' ... */
+ 0x7fffffffffffffffL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 9223372036854775808L,
+ /* expect+1: ... integer 'long' ... */
+ 0x8000000000000000L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 18446744073709551615L,
+ /* expect+1: ... integer 'long' ... */
+ 0xffffffffffffffffL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 18446744073709551616L,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long' ... */
+ 0x00010000000000000000L,
+
+ /* expect+1: ... integer 'long long' ... */
+ 2147483647LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x7fffffffLL,
+ /* expect+1: ... integer 'long long' ... */
+ 2147483648LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x80000000LL,
+ /* expect+1: ... integer 'long long' ... */
+ 4294967295LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0xffffffffLL,
+ /* expect+1: ... integer 'long long' ... */
+ 4294967296LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x0000000100000000LL,
+ /* expect+1: ... integer 'long long' ... */
+ 9223372036854775807LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x7fffffffffffffffLL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long long' ... */
+ 9223372036854775808LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0x8000000000000000LL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long long' ... */
+ 18446744073709551615LL,
+ /* expect+1: ... integer 'long long' ... */
+ 0xffffffffffffffffLL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long long' ... */
+ 18446744073709551616LL,
+ /* expect+2: warning: integer constant out of range [252] */
+ /* expect+1: ... integer 'long long' ... */
+ 0x00010000000000000000LL,
+};
diff --git a/usr.sbin/certctl/Makefile b/usr.sbin/certctl/Makefile
new file mode 100644
index 000000000000..962a472cb281
--- /dev/null
+++ b/usr.sbin/certctl/Makefile
@@ -0,0 +1,10 @@
+# $NetBSD: Makefile,v 1.1 2023/08/26 05:27:14 riastradh Exp $
+#
+
+.include "Makefile.inc" # TESTSDIR
+
+SUBDIR= certs1 certs2 certs3 certs4
+
+TESTS_SH= t_certctl
+
+.include <bsd.test.mk>
diff --git a/usr.sbin/certctl/Makefile.inc b/usr.sbin/certctl/Makefile.inc
new file mode 100644
index 000000000000..b3c142bf5aba
--- /dev/null
+++ b/usr.sbin/certctl/Makefile.inc
@@ -0,0 +1,6 @@
+# $NetBSD: Makefile.inc,v 1.1 2023/08/26 05:27:14 riastradh Exp $
+#
+
+TESTSDIR= ${TESTSBASE}/usr.sbin/certctl
+
+.include <bsd.own.mk>
diff --git a/usr.sbin/certctl/certs1/DigiCert_Global_Root_CA.pem b/usr.sbin/certctl/certs1/DigiCert_Global_Root_CA.pem
new file mode 100644
index 000000000000..fd4341df2663
--- /dev/null
+++ b/usr.sbin/certctl/certs1/DigiCert_Global_Root_CA.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
+QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
+b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
+CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
+nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
+43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
+T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
+gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
+BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
+TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
+DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
+hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
+06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
+PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
+YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
+CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
+-----END CERTIFICATE-----
diff --git a/usr.sbin/certctl/certs1/Explicitly_Distrust_DigiNotar_Root_CA.pem b/usr.sbin/certctl/certs1/Explicitly_Distrust_DigiNotar_Root_CA.pem
new file mode 100644
index 000000000000..ab60ad25fe99
--- /dev/null
+++ b/usr.sbin/certctl/certs1/Explicitly_Distrust_DigiNotar_Root_CA.pem
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFijCCA3KgAwIBAgIQD////////////////////zANBgkqhkiG9w0BAQUFADBf
+MQswCQYDVQQGEwJOTDESMBAGA1UEChMJRGlnaU5vdGFyMRowGAYDVQQDExFEaWdp
+Tm90YXIgUm9vdCBDQTEgMB4GCSqGSIb3DQEJARYRaW5mb0BkaWdpbm90YXIubmww
+HhcNMDcwNzI3MTcxOTM3WhcNMjUwMzMxMTgxOTIyWjBfMQswCQYDVQQGEwJOTDES
+MBAGA1UEChMJRGlnaU5vdGFyMRowGAYDVQQDExFEaWdpTm90YXIgUm9vdCBDQTEg
+MB4GCSqGSIb3DQEJARYRaW5mb0BkaWdpbm90YXIubmwwggIiMA0GCSqGSIb3DQEB
+AQUAA4ICDwAwggIKAoICAQCssFjBAL3YIQgLK5r+blYwBZ8bd5AQQVzDDYcRd46B
+8cp86Yxq7Th0Nbva3/m7wAk3tJZzgX0zGpg595NvlX89ubF1h7pRSOiLcD6VBMXY
+tsMW2YiwsYcdcNqGtA8Ui3rPENF0NqISe3eGSnnme98CEWilToauNFibJBN4ViIl
+HgGLS1Fx+4LMWZZpiFpoU8W5DQI3y0u8ZkqQfioLBQftFl9VkHXYRskbg+IIvvEj
+zJkd1ioPgyAVWCeCLvriIsJJsbkBgWqdbZ1Ad2h2TiEqbYRAhU52mXyC8/O3AlnU
+JgEbjt+tUwbRrhjd4rI6y9eIOI6sWym5GdOY+RgDz0iChmYLG2kPyes4iHomGgVM
+ktck1JbyrFIto0fVUvY//s6EBnCmqj6i8rZWNBhXouSBbefK8GrTx5FrAoNBfBXv
+a5pkXuPQPOWx63tdhvvL5ndJzaNl3Pe5nLjkC1+Tz8wwGjIczhxjlaX56uF0i57p
+K6kwe6AYHw4YC+VbqdPRbB4HZ4+RS6mKvNJmqpMBiLKR+jFc1abBUggJzQpjotMi
+puih2TkGl/VujQKQjBR7P4DNG5y6xFhyI6+2Vp/GekIzKQc/gsnmHwUNzUwoNovT
+yD4cxojvXu6JZOkd69qJfjKmadHdzIif0dDJZiHcBmfFlHqabWJMfczgZICynkeO
+owIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV
+HQ4EFgQUiGi/4I41xDs4a2L3KDuEgcgM100wDQYJKoZIhvcNAQEFBQADggIBADsC
+jcs8MOhuoK3yc7NfniUTBAXT9uOLuwt5zlPe5JbF0a9zvNXD0EBVfEB/zRtfCdXy
+fJ9oHbtdzno5wozWmHvFg1Wo1X1AyuAe94leY12hE8JdiraKfADzI8PthV9xdvBo
+Y6pFITlIYXg23PFDk9Qlx/KAZeFTAnVR/Ho67zerhChXDNjU1JlWbOOi/lmEtDHo
+M/hklJRRl6s5xUvt2t2AC298KQ3EjopyDedTFLJgQT2EkTFoPSdE2+Xe9PpjRchM
+Ppj1P0G6Tss3DbpmmPHdy59c91Q2gmssvBNhl0L4eLvMyKKfyvBovWsdst+Nbwed
+2o5nx0ceyrm/KkKRt2NTZvFCo+H0Wk1Ya7XkpDOtXHAd3ODy63MUkZoDweoAZbwH
+/M8SESIsrqC9OuCiKthZ6SnTGDWkrBFfGbW1G/8iSlzGeuQX7yCpp/Q/rYqnmgQl
+nQ7KN+ZQ/YxCKQSa7LnPS3K94gg2ryMvYuXKAdNw23yCIywWMQzGNgeQerEfZ1jE
+O1hZibCMjFCz2IbLaKPECudpSyDOwR5WS5WpI2jYMNjD67BVUc3l/Su49bsRn1NU
+9jQZjHkJNsphFyUXC4KYcwx3dMPVDceoEkzHp1RxRy4sGn3J4ys7SN4nhKdjNrN9
+j6BkOSQNPXuHr2ZcdBtLc7LljPCGmbjlxd+Ewbfr
+-----END CERTIFICATE-----
diff --git a/usr.sbin/certctl/certs1/Makefile b/usr.sbin/certctl/certs1/Makefile
new file mode 100644
index 000000000000..b75066c9a7b9
--- /dev/null
+++ b/usr.sbin/certctl/certs1/Makefile
@@ -0,0 +1,10 @@
+# $NetBSD: Makefile,v 1.1 2023/08/26 05:27:14 riastradh Exp $
+#
+
+FILESDIR= ${TESTSDIR}/certs1
+
+FILES+= DigiCert_Global_Root_CA.pem
+FILES+= Explicitly_Distrust_DigiNotar_Root_CA.pem
+
+.include <bsd.files.mk>
+.include <bsd.inc.mk>
diff --git a/usr.sbin/certctl/certs2/GTS_Root_R1.pem b/usr.sbin/certctl/certs2/GTS_Root_R1.pem
new file mode 100644
index 000000000000..a13aa0564117
--- /dev/null
+++ b/usr.sbin/certctl/certs2/GTS_Root_R1.pem
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw
+CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU
+MBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw
+MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp
+Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA
+A4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo
+27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w
+Cl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw
+TcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl
+qAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH
+szVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8
+Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk
+MiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92
+wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p
+aDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN
+VjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID
+AQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E
+FgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb
+C5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe
+QkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy
+h6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4
+7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J
+ZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef
+MgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/
+Z6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT
+6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ
+0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm
+2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb
+bP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c
+-----END CERTIFICATE-----
diff --git a/usr.sbin/certctl/certs2/GlobalSign_Root_CA_-_R3.pem b/usr.sbin/certctl/certs2/GlobalSign_Root_CA_-_R3.pem
new file mode 100644
index 000000000000..8afb219058fb
--- /dev/null
+++ b/usr.sbin/certctl/certs2/GlobalSign_Root_CA_-_R3.pem
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G
+A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp
+Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4
+MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG
+A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8
+RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT
+gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm
+KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd
+QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ
+XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw
+DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o
+LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU
+RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp
+jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK
+6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX
+mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs
+Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH
+WD9f
+-----END CERTIFICATE-----
diff --git a/usr.sbin/certctl/certs2/Makefile b/usr.sbin/certctl/certs2/Makefile
new file mode 100644
index 000000000000..8b046afd6ad5
--- /dev/null
+++ b/usr.sbin/certctl/certs2/Makefile
@@ -0,0 +1,10 @@
+# $NetBSD: Makefile,v 1.1 2023/08/26 05:27:14 riastradh Exp $
+#
+
+FILESDIR= ${TESTSDIR}/certs2
+
+FILES+= GTS_Root_R1.pem
+FILES+= GlobalSign_Root_CA_-_R3.pem
+
+.include <bsd.files.mk>
+.include <bsd.inc.mk>
diff --git a/usr.sbin/certctl/certs3/Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.1.pem b/usr.sbin/certctl/certs3/Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.1.pem
new file mode 100644
index 000000000000..842652306722
--- /dev/null
+++ b/usr.sbin/certctl/certs3/Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.1.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGFDCCA/ygAwIBAgIIG3Dp0v+ubHEwDQYJKoZIhvcNAQELBQAwUTELMAkGA1UE
+BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h
+cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0xNDA5MjMxNTIyMDdaFw0zNjA1
+MDUxNTIyMDdaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg
+Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi
+MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9
+thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM
+cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG
+L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i
+NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h
+X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b
+m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy
+Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja
+EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T
+KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF
+6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh
+OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMB0GA1UdDgQWBBRlzeurNR4APn7VdMAc
+tHNHDhpkLzASBgNVHRMBAf8ECDAGAQH/AgEBMIGmBgNVHSAEgZ4wgZswgZgGBFUd
+IAAwgY8wLwYIKwYBBQUHAgEWI2h0dHA6Ly93d3cuZmlybWFwcm9mZXNpb25hbC5j
+b20vY3BzMFwGCCsGAQUFBwICMFAeTgBQAGEAcwBlAG8AIABkAGUAIABsAGEAIABC
+AG8AbgBhAG4AbwB2AGEAIAA0ADcAIABCAGEAcgBjAGUAbABvAG4AYQAgADAAOAAw
+ADEANzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQELBQADggIBAHSHKAIrdx9m
+iWTtj3QuRhy7qPj4Cx2Dtjqn6EWKB7fgPiDL4QjbEwj4KKE1soCzC1HA01aajTNF
+Sa9J8OA9B3pFE1r/yJfY0xgsfZb43aJlQ3CTkBW6kN/oGbDbLIpgD7dvlAceHabJ
+hfa9NPhAeGIQcDq+fUs5gakQ1JZBu/hfHAsdCPKxsIl68veg4MSPi3i1O1ilI45P
+Vf42O+AMt8oqMEEgtIDNrvx2ZnOorm7hfNoD6JQg5iKj0B+QXSBTFCZX2lSX3xZE
+EAEeiGaPcjiT3SC3NL7X8e5jjkd5KAb881lFJWAiMxujX6i6KtoaPc1A6ozuBRWV
+1aUsIC+nmCjuRfzxuIgALI9C2lHVnOUTaHFFQ4ueCyE8S1wF3BqfmI7avSKecs2t
+CsvMo2ebKHTEm9caPARYpoKdrcd7b/+Alun4jWq9GJAd/0kakFI3ky88Al2CdgtR
+5xbHV/g4+afNmyJU72OwFW1TZQNKXkqgsqeOSQBZONXH9IBk9W6VULgRfhVwOEqw
+f9DEMnDAGf/JOC0ULGb0QkTmVXYbgBVX/8Cnp6o5qtjTcNAuuuuUavpfNIbnYrX9
+ivAwhZTJryQCL2/W3Wf+47BVTwSYT6RBVuKT0Gro1vP7ZeDOdcQxWQzugsgMYDNK
+GbqEZycPvEJdvSRUDewdcAZfpLz6IHxV
+-----END CERTIFICATE-----
diff --git a/usr.sbin/certctl/certs3/Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.pem b/usr.sbin/certctl/certs3/Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.pem
new file mode 100644
index 000000000000..de97f841cbfa
--- /dev/null
+++ b/usr.sbin/certctl/certs3/Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE
+BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h
+cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy
+MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg
+Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi
+MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9
+thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM
+cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG
+L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i
+NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h
+X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b
+m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy
+Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja
+EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T
+KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF
+6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh
+OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD
+VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD
+VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp
+cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv
+ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl
+AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF
+661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9
+am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1
+ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481
+PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS
+3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k
+SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF
+3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM
+ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g
+StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz
+Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB
+jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V
+-----END CERTIFICATE-----
diff --git a/usr.sbin/certctl/certs3/Makefile b/usr.sbin/certctl/certs3/Makefile
new file mode 100644
index 000000000000..c9721ef7aa2a
--- /dev/null
+++ b/usr.sbin/certctl/certs3/Makefile
@@ -0,0 +1,10 @@
+# $NetBSD: Makefile,v 1.1 2023/08/26 05:27:14 riastradh Exp $
+#
+
+FILESDIR= ${TESTSDIR}/certs3
+
+FILES+= Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.1.pem
+FILES+= Autoridad_de_Certificacion_Firmaprofesional_CIF_A62634068.pem
+
+.include <bsd.files.mk>
+.include <bsd.inc.mk>
diff --git a/usr.sbin/certctl/certs4/AC_RAIZ_FNMT-RCM.pem b/usr.sbin/certctl/certs4/AC_RAIZ_FNMT-RCM.pem
new file mode 100644
index 000000000000..666cf67c32be
--- /dev/null
+++ b/usr.sbin/certctl/certs4/AC_RAIZ_FNMT-RCM.pem
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx
+CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ
+WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ
+BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG
+Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/
+yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf
+BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz
+WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF
+tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z
+374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC
+IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL
+mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7
+wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS
+MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2
+ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet
+UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw
+AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H
+YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3
+LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD
+nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1
+RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM
+LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf
+77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N
+JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm
+fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp
+6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp
+1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B
+9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok
+RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv
+uu8wd+RU4riEmViAqhOLUTpPSPaLtrM=
+-----END CERTIFICATE-----
diff --git a/usr.sbin/certctl/certs4/DigiCert_Global_Root_CA.pem b/usr.sbin/certctl/certs4/DigiCert_Global_Root_CA.pem
new file mode 100644
index 000000000000..fd4341df2663
--- /dev/null
+++ b/usr.sbin/certctl/certs4/DigiCert_Global_Root_CA.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh
+MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
+d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD
+QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT
+MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
+b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB
+CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97
+nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt
+43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P
+T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4
+gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO
+BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR
+TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw
+DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr
+hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg
+06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF
+PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls
+YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk
+CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=
+-----END CERTIFICATE-----
diff --git a/usr.sbin/certctl/certs4/Makefile b/usr.sbin/certctl/certs4/Makefile
new file mode 100644
index 000000000000..6fe3a4d5f118
--- /dev/null
+++ b/usr.sbin/certctl/certs4/Makefile
@@ -0,0 +1,10 @@
+# $NetBSD: Makefile,v 1.1 2023/08/26 05:27:15 riastradh Exp $
+#
+
+FILESDIR= ${TESTSDIR}/certs4
+
+FILES+= AC_RAIZ_FNMT-RCM.pem
+FILES+= DigiCert_Global_Root_CA.pem
+
+.include <bsd.files.mk>
+.include <bsd.inc.mk>
diff --git a/usr.sbin/certctl/t_certctl.sh b/usr.sbin/certctl/t_certctl.sh
new file mode 100644
index 000000000000..d14a57283404
--- /dev/null
+++ b/usr.sbin/certctl/t_certctl.sh
@@ -0,0 +1,486 @@
+#!/bin/sh
+
+# $NetBSD: t_certctl.sh,v 1.10 2023/09/05 12:32:30 riastradh Exp $
+#
+# Copyright (c) 2023 The NetBSD Foundation, Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+CERTCTL="certctl -C certs.conf -c certs -u untrusted"
+
+# setupconf <subdir>...
+#
+# Create certs/ and set up certs.conf to search the specified
+# subdirectories of the source directory.
+#
+setupconf()
+{
+ local sep subdir dir
+
+ mkdir certs
+ cat <<EOF >certs.conf
+netbsd-certctl 20230816
+
+# comment at line start
+ # comment not at line start, plus some intentional whitespace
+
+# THE WHITESPACE ABOVE IS INTENTIONAL, DO NOT DELETE
+EOF
+ # Start with a continuation line separator; then switch to
+ # non-continuation lines.
+ sep=$(printf ' \\\n\t')
+ for subdir; do
+ dir=$(atf_get_srcdir)/$subdir
+ cat <<EOF >>certs.conf
+path$sep$(printf '%s' "$dir" | vis -M)
+EOF
+ sep=' '
+ done
+}
+
+# check_empty
+#
+# Verify the certs directory is empty after dry runs or after
+# clearing the directory.
+#
+check_empty()
+{
+ local why
+
+ why=${1:-dry run}
+ for x in certs/*; do
+ if [ -e "$x" -o -h "$x" ]; then
+ atf_fail "certs/ should be empty after $why"
+ fi
+ done
+}
+
+# check_nonempty
+#
+# Verify the certs directory is nonempty.
+#
+check_nonempty()
+{
+ for x in certs/*.0; do
+ test -e "$x" && test -h "$x" && return
+ done
+ atf_fail "certs/ should be nonempty"
+}
+
+# checks <certsN>...
+#
+# Run various checks with certctl.
+#
+checks()
+{
+ local certs1 diginotar_base diginotar diginotar_hash subdir srcdir
+
+ certs1=$(atf_get_srcdir)/certs1
+ diginotar_base=Explicitly_Distrust_DigiNotar_Root_CA.pem
+ diginotar=$certs1/$diginotar_base
+ diginotar_hash=$(openssl x509 -hash -noout <$diginotar)
+
+ # Do a dry run of rehash and make sure the directory is still
+ # empty.
+ atf_check -s exit:0 $CERTCTL -n rehash
+ check_empty
+
+ # Distrust and trust one CA, as a dry run. The trust should
+ # fail because it's not currently distrusted.
+ atf_check -s exit:0 $CERTCTL -n untrust "$diginotar"
+ check_empty
+ atf_check -s not-exit:0 -e match:currently \
+ $CERTCTL -n trust "$diginotar"
+ check_empty
+
+ # Do a real rehash, not a dry run.
+ atf_check -s exit:0 $CERTCTL rehash
+
+ # Make sure all the certificates are trusted.
+ for subdir; do
+ case $subdir in
+ /*) srcdir=$subdir;;
+ *) srcdir=$(atf_get_srcdir)/$subdir;;
+ esac
+ for cert in "$srcdir"/*.pem; do
+ # Verify the certificate is linked by its base name.
+ certbase=$(basename "$cert")
+ atf_check -s exit:0 -o inline:"$cert" \
+ readlink -n "certs/$certbase"
+
+ # Verify the certificate is linked by a hash.
+ hash=$(openssl x509 -hash -noout <$cert)
+ counter=0
+ found=false
+ while [ $counter -lt 10 ]; do
+ if cmp -s "certs/$hash.$counter" "$cert"; then
+ found=true
+ break
+ fi
+ counter=$((counter + 1))
+ done
+ if ! $found; then
+ atf_fail "missing $cert"
+ fi
+
+ # Delete both links.
+ rm "certs/$certbase"
+ rm "certs/$hash.$counter"
+ done
+ done
+
+ # Verify the certificate bundle is there with the right
+ # permissions (0644) and delete it.
+ #
+ # XXX Verify its content.
+ atf_check -s exit:0 test -f certs/ca-certificates.crt
+ atf_check -s exit:0 test ! -h certs/ca-certificates.crt
+ atf_check -s exit:0 -o inline:'100644\n' \
+ stat -f %p certs/ca-certificates.crt
+ rm certs/ca-certificates.crt
+
+ # Make sure after deleting everything there's nothing left.
+ check_empty "removing all expected certificates"
+
+ # Distrust, trust, and re-distrust one CA, and verify that it
+ # ceases to appear, reappears, and again ceases to appear.
+ # (This one has no subject hash collisions to worry about, so
+ # we hard-code the `.0' suffix.)
+ atf_check -s exit:0 $CERTCTL untrust "$diginotar"
+ atf_check -s exit:0 test -e "untrusted/$diginotar_base"
+ atf_check -s exit:0 test -h "untrusted/$diginotar_base"
+ atf_check -s exit:0 test ! -e "certs/$diginotar_base"
+ atf_check -s exit:0 test ! -h "certs/$diginotar_base"
+ atf_check -s exit:0 test ! -e "certs/$diginotar_hash.0"
+ atf_check -s exit:0 test ! -h "certs/$diginotar_hash.0"
+ check_nonempty
+
+ atf_check -s exit:0 $CERTCTL trust "$diginotar"
+ atf_check -s exit:0 test ! -e "untrusted/$diginotar_base"
+ atf_check -s exit:0 test ! -h "untrusted/$diginotar_base"
+ atf_check -s exit:0 test -e "certs/$diginotar_base"
+ atf_check -s exit:0 test -h "certs/$diginotar_base"
+ atf_check -s exit:0 test -e "certs/$diginotar_hash.0"
+ atf_check -s exit:0 test -h "certs/$diginotar_hash.0"
+ rm "certs/$diginotar_base"
+ rm "certs/$diginotar_hash.0"
+ check_nonempty
+
+ atf_check -s exit:0 $CERTCTL untrust "$diginotar"
+ atf_check -s exit:0 test -e "untrusted/$diginotar_base"
+ atf_check -s exit:0 test -h "untrusted/$diginotar_base"
+ atf_check -s exit:0 test ! -e "certs/$diginotar_base"
+ atf_check -s exit:0 test ! -h "certs/$diginotar_base"
+ atf_check -s exit:0 test ! -e "certs/$diginotar_hash.0"
+ atf_check -s exit:0 test ! -h "certs/$diginotar_hash.0"
+ check_nonempty
+}
+
+atf_test_case empty
+empty_head()
+{
+ atf_set "descr" "Test empty certificates store"
+}
+empty_body()
+{
+ setupconf # no directories
+ check_empty "empty cert path"
+ atf_check -s exit:0 $CERTCTL -n rehash
+ check_empty
+ atf_check -s exit:0 $CERTCTL rehash
+ atf_check -s exit:0 test -f certs/ca-certificates.crt
+ atf_check -s exit:0 test \! -h certs/ca-certificates.crt
+ atf_check -s exit:0 test \! -s certs/ca-certificates.crt
+ atf_check -s exit:0 rm certs/ca-certificates.crt
+ check_empty "empty cert path"
+}
+
+atf_test_case onedir
+onedir_head()
+{
+ atf_set "descr" "Test one certificates directory"
+}
+onedir_body()
+{
+ setupconf certs1
+ checks certs1
+}
+
+atf_test_case twodir
+twodir_head()
+{
+ atf_set "descr" "Test two certificates directories"
+}
+twodir_body()
+{
+ setupconf certs1 certs2
+ checks certs1 certs2
+}
+
+atf_test_case collidehash
+collidehash_head()
+{
+ atf_set "descr" "Test colliding hashes"
+}
+collidehash_body()
+{
+ # certs3 has two certificates with the same subject hash
+ setupconf certs1 certs3
+ checks certs1 certs3
+}
+
+atf_test_case collidebase
+collidebase_head()
+{
+ atf_set "descr" "Test colliding base names"
+}
+collidebase_body()
+{
+ # certs1 and certs4 both have DigiCert_Global_Root_CA.pem,
+ # which should cause list and rehash to fail and mention
+ # duplicates.
+ setupconf certs1 certs4
+ atf_check -s not-exit:0 -o ignore -e match:duplicate $CERTCTL list
+ atf_check -s not-exit:0 -o ignore -e match:duplicate $CERTCTL rehash
+}
+
+atf_test_case manual
+manual_head()
+{
+ atf_set "descr" "Test manual operation"
+}
+manual_body()
+{
+ local certs1 diginotar_base diginotar diginotar_hash
+
+ certs1=$(atf_get_srcdir)/certs1
+ diginotar_base=Explicitly_Distrust_DigiNotar_Root_CA.pem
+ diginotar=$certs1/$diginotar_base
+ diginotar_hash=$(openssl x509 -hash -noout <$diginotar)
+
+ setupconf certs1 certs2
+ cat <<EOF >>certs.conf
+manual
+EOF
+ touch certs/bogus.pem
+ ln -s bogus.pem certs/0123abcd.0
+
+ # Listing shouldn't mention anything in the certs/ cache.
+ atf_check -s exit:0 -o not-match:bogus $CERTCTL list
+ atf_check -s exit:0 -o not-match:bogus $CERTCTL untrusted
+
+ # Rehashing and changing the configuration should succeed, but
+ # mention `manual' in a warning message and should not touch
+ # the cache.
+ atf_check -s exit:0 -e match:manual $CERTCTL rehash
+ atf_check -s exit:0 -e match:manual $CERTCTL untrust "$diginotar"
+ atf_check -s exit:0 -e match:manual $CERTCTL trust "$diginotar"
+
+ # The files we created should still be there.
+ atf_check -s exit:0 test -f certs/bogus.pem
+ atf_check -s exit:0 test -h certs/0123abcd.0
+}
+
+atf_test_case evilcertsdir
+evilcertsdir_head()
+{
+ atf_set "descr" "Test certificate directory with evil characters"
+}
+evilcertsdir_body()
+{
+ local certs1 diginotar_base diginotar evilcertsdir evildistrustdir
+
+ certs1=$(atf_get_srcdir)/certs1
+ diginotar_base=Explicitly_Distrust_DigiNotar_Root_CA.pem
+ diginotar=$certs1/$diginotar_base
+
+ evilcertsdir=$(printf '-evil certs\n.')
+ evilcertsdir=${evilcertsdir%.}
+ evildistrustdir=$(printf '-evil untrusted\n.')
+ evildistrustdir=${evildistrustdir%.}
+
+ setupconf certs1
+
+ # initial (re)hash, nonexistent certs directory
+ atf_check -s exit:0 $CERTCTL rehash
+ atf_check -s exit:0 certctl -C certs.conf \
+ -c "$evilcertsdir" -u "$evildistrustdir" \
+ rehash
+ atf_check -s exit:0 diff -ruN -- certs "$evilcertsdir"
+ atf_check -s exit:0 test ! -e untrusted
+ atf_check -s exit:0 test ! -h untrusted
+ atf_check -s exit:0 test ! -e "$evildistrustdir"
+ atf_check -s exit:0 test ! -h "$evildistrustdir"
+
+ # initial (re)hash, empty certs directory
+ atf_check -s exit:0 rm -rf -- certs
+ atf_check -s exit:0 rm -rf -- "$evilcertsdir"
+ atf_check -s exit:0 mkdir -- certs
+ atf_check -s exit:0 mkdir -- "$evilcertsdir"
+ atf_check -s exit:0 $CERTCTL rehash
+ atf_check -s exit:0 certctl -C certs.conf \
+ -c "$evilcertsdir" -u "$evildistrustdir" \
+ rehash
+ atf_check -s exit:0 diff -ruN -- certs "$evilcertsdir"
+ atf_check -s exit:0 test ! -e untrusted
+ atf_check -s exit:0 test ! -h untrusted
+ atf_check -s exit:0 test ! -e "$evildistrustdir"
+ atf_check -s exit:0 test ! -h "$evildistrustdir"
+
+ # test distrusting a CA
+ atf_check -s exit:0 $CERTCTL untrust "$diginotar"
+ atf_check -s exit:0 certctl -C certs.conf \
+ -c "$evilcertsdir" -u "$evildistrustdir" \
+ untrust "$diginotar"
+ atf_check -s exit:0 diff -ruN -- certs "$evilcertsdir"
+ atf_check -s exit:0 diff -ruN -- untrusted "$evildistrustdir"
+
+ # second rehash
+ atf_check -s exit:0 $CERTCTL rehash
+ atf_check -s exit:0 certctl -C certs.conf \
+ -c "$evilcertsdir" -u "$evildistrustdir" \
+ rehash
+ atf_check -s exit:0 diff -ruN -- certs "$evilcertsdir"
+ atf_check -s exit:0 diff -ruN -- untrusted "$evildistrustdir"
+}
+
+atf_test_case evilpath
+evilpath_head()
+{
+ atf_set "descr" "Test certificate paths with evil characters"
+}
+evilpath_body()
+{
+ local evildir
+
+ evildir=$(printf 'evil\n.')
+ evildir=${evildir%.}
+ mkdir "$evildir"
+
+ cp -p "$(atf_get_srcdir)/certs2"/*.pem "$evildir"/
+
+ setupconf certs1
+ cat <<EOF >>certs.conf
+path $(printf '%s' "$(pwd)/$evildir" | vis -M)
+EOF
+ checks certs1 "$(pwd)/$evildir"
+}
+
+atf_test_case missingconf
+missingconf_head()
+{
+ atf_set "descr" "Test certctl with missing certs.conf"
+}
+missingconf_body()
+{
+ mkdir certs
+ atf_check -s exit:0 test ! -e certs.conf
+ atf_check -s not-exit:0 -e match:'certs\.conf' \
+ $CERTCTL rehash
+}
+
+atf_test_case nonexistentcertsdir
+nonexistentcertsdir_head()
+{
+ atf_set "descr" "Test certctl succeeds when certsdir is nonexistent"
+}
+nonexistentcertsdir_body()
+{
+ setupconf certs1
+ rmdir certs
+ checks certs1
+}
+
+atf_test_case symlinkcertsdir
+symlinkcertsdir_head()
+{
+ atf_set "descr" "Test certctl fails when certsdir is a symlink"
+}
+symlinkcertsdir_body()
+{
+ setupconf certs1
+ rmdir certs
+ mkdir empty
+ ln -sfn empty certs
+
+ atf_check -s not-exit:0 -e match:symlink $CERTCTL -n rehash
+ atf_check -s not-exit:0 -e match:symlink $CERTCTL rehash
+ atf_check -s exit:0 rmdir empty
+}
+
+atf_test_case regularfilecertsdir
+regularfilecertsdir_head()
+{
+ atf_set "descr" "Test certctl fails when certsdir is a regular file"
+}
+regularfilecertsdir_body()
+{
+ setupconf certs1
+ rmdir certs
+ echo 'hello world' >certs
+
+ atf_check -s not-exit:0 -e match:directory $CERTCTL -n rehash
+ atf_check -s not-exit:0 -e match:directory $CERTCTL rehash
+ atf_check -s exit:0 rm certs
+}
+
+atf_test_case prepopulatedcerts
+prepopulatedcerts_head()
+{
+ atf_set "descr" "Test certctl fails when directory is prepopulated"
+}
+prepopulatedcerts_body()
+{
+ local cert certbase target
+
+ setupconf certs1
+ ln -sfn "$(atf_get_srcdir)/certs2"/*.pem certs/
+
+ atf_check -s not-exit:0 -e match:manual $CERTCTL -n rehash
+ atf_check -s not-exit:0 -e match:manual $CERTCTL rehash
+ for cert in "$(atf_get_srcdir)/certs2"/*.pem; do
+ certbase=$(basename "$cert")
+ atf_check -s exit:0 -o inline:"$cert" \
+ readlink -n "certs/$certbase"
+ rm "certs/$certbase"
+ done
+ check_empty
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case collidebase
+ atf_add_test_case collidehash
+ atf_add_test_case empty
+ atf_add_test_case evilcertsdir
+ atf_add_test_case evilpath
+ atf_add_test_case manual
+ atf_add_test_case missingconf
+ atf_add_test_case nonexistentcertsdir
+ atf_add_test_case onedir
+ atf_add_test_case prepopulatedcerts
+ atf_add_test_case regularfilecertsdir
+ atf_add_test_case symlinkcertsdir
+ atf_add_test_case twodir
+}
diff --git a/usr.sbin/makefs/Makefile b/usr.sbin/makefs/Makefile
new file mode 100644
index 000000000000..c614c26b6208
--- /dev/null
+++ b/usr.sbin/makefs/Makefile
@@ -0,0 +1,8 @@
+# $NetBSD: Makefile,v 1.1 2024/10/18 23:22:52 christos Exp $
+
+.include <bsd.own.mk>
+
+TESTSDIR= ${TESTSBASE}/usr.sbin/makefs
+TESTS_SH= t_makefs
+
+.include <bsd.test.mk>
diff --git a/usr.sbin/makefs/t_makefs.sh b/usr.sbin/makefs/t_makefs.sh
new file mode 100644
index 000000000000..cf5d6fda54e3
--- /dev/null
+++ b/usr.sbin/makefs/t_makefs.sh
@@ -0,0 +1,65 @@
+# $NetBSD: t_makefs.sh,v 1.1 2024/10/18 23:22:52 christos Exp $
+#
+# Copyright (c) 2024 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.
+#
+
+
+# replace
+#
+atf_test_case replace replace_cleanup
+
+replace_head() {
+ atf_require_prog makefs
+ atf_set "descr" "Test replacing a single file (PR/58759)"
+}
+
+replace_body() {
+ export PATH=/sbin:/bin:/usr/sbin:/usr/bin
+ mkdir -p 1 2/1
+ echo foo > 1/foo
+ echo bar > 2/1/foo
+ makefs -r -d 0xf0 -s 1m -t ffs foo.img 1 2/1
+ dump 0f foo.dump -F foo.img
+ restore -rf foo.dump
+ if [ ! -f foo ]; then
+ atf_fail "file foo does not exist"
+ fi
+ x=$(cat foo)
+ if [ "$x" != "bar" ]; then
+ atf_fail "file foo does not contain bar, but $x"
+ fi
+ atf_pass
+}
+
+replace_cleanup() {
+ rm -fr 1 2 foo foo.dump foo.img
+}
+
+atf_init_test_cases() {
+ atf_add_test_case replace
+}