1*af25f247SDevin Teske# 2*af25f247SDevin Teske# SPDX-License-Identifier: BSD-2-Clause 3*af25f247SDevin Teske# 4*af25f247SDevin Teske# Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org> 5*af25f247SDevin Teske# 6*af25f247SDevin Teske 7*af25f247SDevin Teske# The system calls a program makes vary with the machine and with the 8*af25f247SDevin Teske# run-time linker, so these tests assert which system calls -t may and 9*af25f247SDevin Teske# may not report rather than the exact sequence of them. 10*af25f247SDevin Teske 11*af25f247SDevin Teskerequire_truss() 12*af25f247SDevin Teske{ 13*af25f247SDevin Teske truss -o /dev/null /usr/bin/true >/dev/null 2>&1 || 14*af25f247SDevin Teske atf_skip "unable to trace a child process here" 15*af25f247SDevin Teske} 16*af25f247SDevin Teske 17*af25f247SDevin Teske# Write the sorted, unique names of the system calls reported in a truss 18*af25f247SDevin Teske# output file to another file. 19*af25f247SDevin Teskesyscall_names() 20*af25f247SDevin Teske{ 21*af25f247SDevin Teske sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_.]*\)(.*$/\1/p' "$1" | sort -u > "$2" 22*af25f247SDevin Teske} 23*af25f247SDevin Teske 24*af25f247SDevin Teske# Fail unless every name in a file matches an extended regular expression. 25*af25f247SDevin Teskeonly_names() 26*af25f247SDevin Teske{ 27*af25f247SDevin Teske if grep -Ev "$1" "$2" > unexpected; then 28*af25f247SDevin Teske atf_fail "reported system calls not selected by the filter:" \ 29*af25f247SDevin Teske "$(tr '\n' ' ' < unexpected)" 30*af25f247SDevin Teske fi 31*af25f247SDevin Teske} 32*af25f247SDevin Teske 33*af25f247SDevin Teske# Fail if any name in a file matches an extended regular expression. 34*af25f247SDevin Teskeno_name() 35*af25f247SDevin Teske{ 36*af25f247SDevin Teske if grep -E "$1" "$2" > unexpected; then 37*af25f247SDevin Teske atf_fail "system calls excluded by the filter were reported:" \ 38*af25f247SDevin Teske "$(tr '\n' ' ' < unexpected)" 39*af25f247SDevin Teske fi 40*af25f247SDevin Teske} 41*af25f247SDevin Teske 42*af25f247SDevin Teske# Fail unless at least one name in a file matches. 43*af25f247SDevin Teskesome_name() 44*af25f247SDevin Teske{ 45*af25f247SDevin Teske grep -Eq "$1" "$2" || 46*af25f247SDevin Teske atf_fail "no system call matching $1 was reported" 47*af25f247SDevin Teske} 48*af25f247SDevin Teske 49*af25f247SDevin Teskeatf_test_case list 50*af25f247SDevin Teskelist_head() 51*af25f247SDevin Teske{ 52*af25f247SDevin Teske atf_set descr "-t with no expression prints the available groups" 53*af25f247SDevin Teske} 54*af25f247SDevin Teskelist_body() 55*af25f247SDevin Teske{ 56*af25f247SDevin Teske atf_check -s exit:2 -o match:'@all' -o match:'@none' \ 57*af25f247SDevin Teske -o match:'@read' -o match:'@write' -o match:'@file' \ 58*af25f247SDevin Teske -o match:'@net' \ 59*af25f247SDevin Teske truss -t 60*af25f247SDevin Teske} 61*af25f247SDevin Teske 62*af25f247SDevin Teskeatf_test_case unknown_group 63*af25f247SDevin Teskeunknown_group_head() 64*af25f247SDevin Teske{ 65*af25f247SDevin Teske atf_set descr "an unknown @group is rejected" 66*af25f247SDevin Teske} 67*af25f247SDevin Teskeunknown_group_body() 68*af25f247SDevin Teske{ 69*af25f247SDevin Teske atf_check -s exit:1 -e match:'unknown system call group @nosuch' \ 70*af25f247SDevin Teske truss -t @nosuch /usr/bin/true 71*af25f247SDevin Teske} 72*af25f247SDevin Teske 73*af25f247SDevin Teskeatf_test_case empty_term 74*af25f247SDevin Teskeempty_term_head() 75*af25f247SDevin Teske{ 76*af25f247SDevin Teske atf_set descr "an empty term adds nothing to the expression" 77*af25f247SDevin Teske} 78*af25f247SDevin Teskeempty_term_body() 79*af25f247SDevin Teske{ 80*af25f247SDevin Teske require_truss 81*af25f247SDevin Teske printf 'hello\n' > input 82*af25f247SDevin Teske 83*af25f247SDevin Teske # An empty expression filters nothing, as if -t were absent. 84*af25f247SDevin Teske atf_check -s exit:0 -o inline:"hello\n" truss -o out -t '' cat input 85*af25f247SDevin Teske syscall_names out names 86*af25f247SDevin Teske some_name '^read$' names 87*af25f247SDevin Teske some_name '^openat$' names 88*af25f247SDevin Teske 89*af25f247SDevin Teske # A stray comma is ignored rather than being an error. 90*af25f247SDevin Teske atf_check -s exit:0 -o inline:"hello\n" \ 91*af25f247SDevin Teske truss -o out2 -t ',read,,write,' cat input 92*af25f247SDevin Teske syscall_names out2 names2 93*af25f247SDevin Teske only_names '^(read|write)$' names2 94*af25f247SDevin Teske some_name '^read$' names2 95*af25f247SDevin Teske 96*af25f247SDevin Teske # A negation with nothing to negate is still a mistake. 97*af25f247SDevin Teske atf_check -s exit:1 -e match:"missing pattern after" \ 98*af25f247SDevin Teske truss -t '!' /usr/bin/true 99*af25f247SDevin Teske} 100*af25f247SDevin Teske 101*af25f247SDevin Teskeatf_test_case none 102*af25f247SDevin Teskenone_head() 103*af25f247SDevin Teske{ 104*af25f247SDevin Teske atf_set descr "@none selects no system call" 105*af25f247SDevin Teske} 106*af25f247SDevin Teskenone_body() 107*af25f247SDevin Teske{ 108*af25f247SDevin Teske require_truss 109*af25f247SDevin Teske printf 'hello\n' > input 110*af25f247SDevin Teske 111*af25f247SDevin Teske # @none's member list is the single negated member "!*", so this 112*af25f247SDevin Teske # also covers a group whose members exclude rather than include. 113*af25f247SDevin Teske atf_check -s exit:0 -o inline:"hello\n" truss -o out -t @none cat input 114*af25f247SDevin Teske syscall_names out names 115*af25f247SDevin Teske atf_check -o empty cat names 116*af25f247SDevin Teske 117*af25f247SDevin Teske atf_check -s exit:0 -o inline:"hello\n" \ 118*af25f247SDevin Teske truss -o out2 -t '!@none' cat input 119*af25f247SDevin Teske syscall_names out2 names2 120*af25f247SDevin Teske some_name '^read$' names2 121*af25f247SDevin Teske some_name '^openat$' names2 122*af25f247SDevin Teske 123*af25f247SDevin Teske # @none is the empty set rather than a switch: it selects nothing 124*af25f247SDevin Teske # and leaves the terms before it alone. 125*af25f247SDevin Teske atf_check -s exit:0 -o inline:"hello\n" \ 126*af25f247SDevin Teske truss -o out3 -t '@file,@none' cat input 127*af25f247SDevin Teske syscall_names out3 names3 128*af25f247SDevin Teske some_name '^openat$' names3 129*af25f247SDevin Teske} 130*af25f247SDevin Teske 131*af25f247SDevin Teskeatf_test_case by_number 132*af25f247SDevin Teskeby_number_head() 133*af25f247SDevin Teske{ 134*af25f247SDevin Teske atf_set descr "a term may name a system call by number" 135*af25f247SDevin Teske} 136*af25f247SDevin Teskeby_number_body() 137*af25f247SDevin Teske{ 138*af25f247SDevin Teske require_truss 139*af25f247SDevin Teske printf 'hello\n' > input 140*af25f247SDevin Teske 141*af25f247SDevin Teske # 3 and 4 have been read(2) and write(2) since 4.2BSD. 142*af25f247SDevin Teske atf_check -s exit:0 -o inline:"hello\n" -e empty \ 143*af25f247SDevin Teske truss -o out -t 3 cat input 144*af25f247SDevin Teske syscall_names out names 145*af25f247SDevin Teske only_names '^read$' names 146*af25f247SDevin Teske some_name '^read$' names 147*af25f247SDevin Teske 148*af25f247SDevin Teske atf_check -s exit:0 -o inline:"hello\n" -e empty \ 149*af25f247SDevin Teske truss -o out2 -t 3,4 cat input 150*af25f247SDevin Teske syscall_names out2 names2 151*af25f247SDevin Teske only_names '^(read|write)$' names2 152*af25f247SDevin Teske 153*af25f247SDevin Teske # Numeric terms negate like any other. 154*af25f247SDevin Teske atf_check -s exit:0 -o inline:"hello\n" -e empty \ 155*af25f247SDevin Teske truss -o out3 -t '!3' cat input 156*af25f247SDevin Teske syscall_names out3 names3 157*af25f247SDevin Teske no_name '^read$' names3 158*af25f247SDevin Teske some_name '.' names3 159*af25f247SDevin Teske 160*af25f247SDevin Teske # Leading zeroes are still just a number. 161*af25f247SDevin Teske atf_check -s exit:0 -o inline:"hello\n" -e empty \ 162*af25f247SDevin Teske truss -o out4 -t 003 cat input 163*af25f247SDevin Teske syscall_names out4 names4 164*af25f247SDevin Teske only_names '^read$' names4 165*af25f247SDevin Teske} 166*af25f247SDevin Teske 167*af25f247SDevin Teskeatf_test_case bad_number 168*af25f247SDevin Teskebad_number_head() 169*af25f247SDevin Teske{ 170*af25f247SDevin Teske atf_set descr "only an unrepresentable system call number is rejected" 171*af25f247SDevin Teske} 172*af25f247SDevin Teskebad_number_body() 173*af25f247SDevin Teske{ 174*af25f247SDevin Teske require_truss 175*af25f247SDevin Teske printf 'hello\n' > input 176*af25f247SDevin Teske 177*af25f247SDevin Teske # A process may issue any number the kernel can hold, so a number 178*af25f247SDevin Teske # beyond the tables truss knows is not second-guessed. It simply 179*af25f247SDevin Teske # does not match anything this program happens to call. 180*af25f247SDevin Teske atf_check -s exit:0 -o inline:"hello\n" -e empty \ 181*af25f247SDevin Teske truss -o out -t 99999 cat input 182*af25f247SDevin Teske syscall_names out names 183*af25f247SDevin Teske atf_check -o empty cat names 184*af25f247SDevin Teske 185*af25f247SDevin Teske # Too large to be a system call number at all: an error. 186*af25f247SDevin Teske atf_check -s exit:1 -e match:'system call number is too large' \ 187*af25f247SDevin Teske truss -t 4294967296 /usr/bin/true 188*af25f247SDevin Teske} 189*af25f247SDevin Teske 190*af25f247SDevin Teskeatf_test_case unknown_syscall 191*af25f247SDevin Teskeunknown_syscall_head() 192*af25f247SDevin Teske{ 193*af25f247SDevin Teske atf_set descr "a term matching no system call warns but still runs" 194*af25f247SDevin Teske} 195*af25f247SDevin Teskeunknown_syscall_body() 196*af25f247SDevin Teske{ 197*af25f247SDevin Teske require_truss 198*af25f247SDevin Teske printf 'hello\n' > input 199*af25f247SDevin Teske 200*af25f247SDevin Teske # A typo is a warning, not an error: the command still runs. 201*af25f247SDevin Teske atf_check -s exit:0 -o inline:"hello\n" \ 202*af25f247SDevin Teske -e match:'opne: matches no known system call' \ 203*af25f247SDevin Teske truss -o out -t opne cat input 204*af25f247SDevin Teske syscall_names out names 205*af25f247SDevin Teske atf_check -o empty cat names 206*af25f247SDevin Teske 207*af25f247SDevin Teske # So is a pattern that can never match. 208*af25f247SDevin Teske atf_check -s exit:0 -o inline:"hello\n" \ 209*af25f247SDevin Teske -e match:'matches no known system call' \ 210*af25f247SDevin Teske truss -o out2 -t 'raed*' cat input 211*af25f247SDevin Teske 212*af25f247SDevin Teske # Names of every ABI truss knows are accepted without complaint, 213*af25f247SDevin Teske # whether or not that ABI is the one being traced here. 214*af25f247SDevin Teske for name in read openat linux_write linux_newstat compat11.stat; do 215*af25f247SDevin Teske atf_check -s exit:0 -o inline:"hello\n" -e empty \ 216*af25f247SDevin Teske truss -o out3 -t "$name" cat input 217*af25f247SDevin Teske done 218*af25f247SDevin Teske 219*af25f247SDevin Teske # A number is the way to name a system call by number; '#' is not 220*af25f247SDevin Teske # a prefix truss accepts, so it is diagnosed like any other typo. 221*af25f247SDevin Teske atf_check -s exit:0 -o inline:"hello\n" \ 222*af25f247SDevin Teske -e match:'matches no known system call' \ 223*af25f247SDevin Teske truss -o out4 -t '#237' cat input 224*af25f247SDevin Teske} 225*af25f247SDevin Teske 226*af25f247SDevin Teskeatf_test_case by_name 227*af25f247SDevin Teskeby_name_head() 228*af25f247SDevin Teske{ 229*af25f247SDevin Teske atf_set descr "a term naming one system call selects only that one" 230*af25f247SDevin Teske} 231*af25f247SDevin Teskeby_name_body() 232*af25f247SDevin Teske{ 233*af25f247SDevin Teske require_truss 234*af25f247SDevin Teske printf 'hello\n' > input 235*af25f247SDevin Teske 236*af25f247SDevin Teske atf_check -s exit:0 -o inline:"hello\n" truss -o out -t read cat input 237*af25f247SDevin Teske syscall_names out names 238*af25f247SDevin Teske only_names '^read$' names 239*af25f247SDevin Teske some_name '^read$' names 240*af25f247SDevin Teske} 241*af25f247SDevin Teske 242*af25f247SDevin Teskeatf_test_case by_pattern 243*af25f247SDevin Teskeby_pattern_head() 244*af25f247SDevin Teske{ 245*af25f247SDevin Teske atf_set descr "a term may be an fnmatch(3) pattern" 246*af25f247SDevin Teske} 247*af25f247SDevin Teskeby_pattern_body() 248*af25f247SDevin Teske{ 249*af25f247SDevin Teske require_truss 250*af25f247SDevin Teske atf_check ln -s target link 251*af25f247SDevin Teske 252*af25f247SDevin Teske atf_check -s exit:0 -o ignore truss -o out -t 'readlink*' readlink link 253*af25f247SDevin Teske syscall_names out names 254*af25f247SDevin Teske only_names '^readlink' names 255*af25f247SDevin Teske some_name '^readlink' names 256*af25f247SDevin Teske} 257*af25f247SDevin Teske 258*af25f247SDevin Teskeatf_test_case group 259*af25f247SDevin Teskegroup_head() 260*af25f247SDevin Teske{ 261*af25f247SDevin Teske atf_set descr "an @group selects the system calls it names" 262*af25f247SDevin Teske} 263*af25f247SDevin Teskegroup_body() 264*af25f247SDevin Teske{ 265*af25f247SDevin Teske require_truss 266*af25f247SDevin Teske printf 'hello\n' > input 267*af25f247SDevin Teske 268*af25f247SDevin Teske atf_check -s exit:0 -o inline:"hello\n" truss -o out -t @read cat input 269*af25f247SDevin Teske syscall_names out names 270*af25f247SDevin Teske some_name '^read$' names 271*af25f247SDevin Teske no_name '^(openat|close|mmap|munmap|mprotect)$' names 272*af25f247SDevin Teske} 273*af25f247SDevin Teske 274*af25f247SDevin Teskeatf_test_case group_read_excludes_readlink 275*af25f247SDevin Teskegroup_read_excludes_readlink_head() 276*af25f247SDevin Teske{ 277*af25f247SDevin Teske atf_set descr "@read selects I/O reads but not readlink(2)" 278*af25f247SDevin Teske} 279*af25f247SDevin Teskegroup_read_excludes_readlink_body() 280*af25f247SDevin Teske{ 281*af25f247SDevin Teske require_truss 282*af25f247SDevin Teske atf_check ln -s target link 283*af25f247SDevin Teske 284*af25f247SDevin Teske # readlink(1) calls readlink(2), which @read must not select even 285*af25f247SDevin Teske # though "read*" does. 286*af25f247SDevin Teske atf_check -s exit:0 -o ignore truss -o out -t @read readlink link 287*af25f247SDevin Teske syscall_names out names 288*af25f247SDevin Teske no_name '^readlink' names 289*af25f247SDevin Teske some_name '^read$' names 290*af25f247SDevin Teske 291*af25f247SDevin Teske atf_check -s exit:0 -o ignore truss -o out2 -t 'read*' readlink link 292*af25f247SDevin Teske syscall_names out2 names2 293*af25f247SDevin Teske some_name '^readlink' names2 294*af25f247SDevin Teske} 295*af25f247SDevin Teske 296*af25f247SDevin Teskeatf_test_case group_reference 297*af25f247SDevin Teskegroup_reference_head() 298*af25f247SDevin Teske{ 299*af25f247SDevin Teske atf_set descr "@desc includes the members of @read and @write" 300*af25f247SDevin Teske} 301*af25f247SDevin Teskegroup_reference_body() 302*af25f247SDevin Teske{ 303*af25f247SDevin Teske require_truss 304*af25f247SDevin Teske printf 'hello\n' > input 305*af25f247SDevin Teske 306*af25f247SDevin Teske atf_check -s exit:0 -o inline:"hello\n" truss -o out -t @desc cat input 307*af25f247SDevin Teske syscall_names out names 308*af25f247SDevin Teske some_name '^read$' names 309*af25f247SDevin Teske some_name '^close$' names 310*af25f247SDevin Teske} 311*af25f247SDevin Teske 312*af25f247SDevin Teskeatf_test_case negation 313*af25f247SDevin Teskenegation_head() 314*af25f247SDevin Teske{ 315*af25f247SDevin Teske atf_set descr "a term prefixed with ! excludes what it matches" 316*af25f247SDevin Teske} 317*af25f247SDevin Teskenegation_body() 318*af25f247SDevin Teske{ 319*af25f247SDevin Teske require_truss 320*af25f247SDevin Teske 321*af25f247SDevin Teske atf_check -s exit:0 -o ignore truss -o out -t '!@all' /usr/bin/true 322*af25f247SDevin Teske syscall_names out names 323*af25f247SDevin Teske atf_check -o empty cat names 324*af25f247SDevin Teske 325*af25f247SDevin Teske atf_check -s exit:0 -o ignore truss -o out2 -t '!@memory' /usr/bin/true 326*af25f247SDevin Teske syscall_names out2 names2 327*af25f247SDevin Teske no_name '^(mmap|munmap|mprotect)$' names2 328*af25f247SDevin Teske some_name '.' names2 329*af25f247SDevin Teske} 330*af25f247SDevin Teske 331*af25f247SDevin Teskeatf_test_case order 332*af25f247SDevin Teskeorder_head() 333*af25f247SDevin Teske{ 334*af25f247SDevin Teske atf_set descr "the last term to match a system call wins" 335*af25f247SDevin Teske} 336*af25f247SDevin Teskeorder_body() 337*af25f247SDevin Teske{ 338*af25f247SDevin Teske require_truss 339*af25f247SDevin Teske printf 'hello\n' > input 340*af25f247SDevin Teske 341*af25f247SDevin Teske atf_check -s exit:0 -o ignore truss -o out -t 'read,!read' cat input 342*af25f247SDevin Teske syscall_names out names 343*af25f247SDevin Teske atf_check -o empty cat names 344*af25f247SDevin Teske 345*af25f247SDevin Teske atf_check -s exit:0 -o ignore truss -o out2 -t '!read,read' cat input 346*af25f247SDevin Teske syscall_names out2 names2 347*af25f247SDevin Teske only_names '^read$' names2 348*af25f247SDevin Teske some_name '^read$' names2 349*af25f247SDevin Teske} 350*af25f247SDevin Teske 351*af25f247SDevin Teskeatf_test_case accumulate 352*af25f247SDevin Teskeaccumulate_head() 353*af25f247SDevin Teske{ 354*af25f247SDevin Teske atf_set descr "repeating -t appends to the expression" 355*af25f247SDevin Teske} 356*af25f247SDevin Teskeaccumulate_body() 357*af25f247SDevin Teske{ 358*af25f247SDevin Teske require_truss 359*af25f247SDevin Teske atf_check ln -s target link 360*af25f247SDevin Teske 361*af25f247SDevin Teske atf_check -s exit:0 -o ignore \ 362*af25f247SDevin Teske truss -o out -t read -t readlink readlink link 363*af25f247SDevin Teske syscall_names out names 364*af25f247SDevin Teske only_names '^(read|readlink)$' names 365*af25f247SDevin Teske some_name '^read$' names 366*af25f247SDevin Teske some_name '^readlink$' names 367*af25f247SDevin Teske} 368*af25f247SDevin Teske 369*af25f247SDevin Teskeatf_test_case count 370*af25f247SDevin Teskecount_head() 371*af25f247SDevin Teske{ 372*af25f247SDevin Teske atf_set descr "-c counts only the selected system calls" 373*af25f247SDevin Teske} 374*af25f247SDevin Teskecount_body() 375*af25f247SDevin Teske{ 376*af25f247SDevin Teske require_truss 377*af25f247SDevin Teske printf 'hello\n' > input 378*af25f247SDevin Teske 379*af25f247SDevin Teske atf_check -s exit:0 -o inline:"hello\n" \ 380*af25f247SDevin Teske truss -c -o out -t read cat input 381*af25f247SDevin Teske atf_check -o match:'^read ' cat out 382*af25f247SDevin Teske atf_check -s exit:1 -o empty grep -q '^openat' out 383*af25f247SDevin Teske} 384*af25f247SDevin Teske 385*af25f247SDevin Teskeatf_init_test_cases() 386*af25f247SDevin Teske{ 387*af25f247SDevin Teske atf_add_test_case list 388*af25f247SDevin Teske atf_add_test_case unknown_group 389*af25f247SDevin Teske atf_add_test_case empty_term 390*af25f247SDevin Teske atf_add_test_case none 391*af25f247SDevin Teske atf_add_test_case by_number 392*af25f247SDevin Teske atf_add_test_case bad_number 393*af25f247SDevin Teske atf_add_test_case unknown_syscall 394*af25f247SDevin Teske atf_add_test_case by_name 395*af25f247SDevin Teske atf_add_test_case by_pattern 396*af25f247SDevin Teske atf_add_test_case group 397*af25f247SDevin Teske atf_add_test_case group_read_excludes_readlink 398*af25f247SDevin Teske atf_add_test_case group_reference 399*af25f247SDevin Teske atf_add_test_case negation 400*af25f247SDevin Teske atf_add_test_case order 401*af25f247SDevin Teske atf_add_test_case accumulate 402*af25f247SDevin Teske atf_add_test_case count 403*af25f247SDevin Teske} 404