# BST-TIMEOUT: 600 # measured 52 s on target (==BST-SUITE, 2026-08-14): 28 # # structural cases, each a process spawn, ahead of a 10-case # # upstream suite whose source arm walks a 468-file tree eight # # times. 600 is ~11x that, which is the headroom this needs # # rather than the 1200 first guessed -- and it is 52 s only # # because of the argv workaround below; without it the source # # arm does not finish at all (linux-in-the-browser#150). . "$BST_LIB" # pax-utils 1.3.3 -- the Gentoo ELF security-scanning toolset scanelf, dumpelf, # pspax and scanmacho (meta-oe recipes-devtools/pax-utils). See # pax-utils_%.bbappend for what the port adds and why. # # --------------------------------------------------------------------------- # WHAT THIS SUITE IS, AND WHY IT IS NOT A PROBE # # It closes on the PACKAGE'S OWN UPSTREAM SUITE. pax-utils ships tests/ with one # arm per subdirectory -- lddtree, scanelf, source -- each a Makefile and a # `dotest` shell script, driven from the top-level check-hook. run-ptest # reproduces that check-hook line verbatim, so which arms exist is still # $(wildcard */Makefile) in upstream's tests/Makefile and which cases each runs # is still its own dotest. The PASS:/FAIL: lines this file parses are printed by # upstream's own tests/lib.sh, not by anything written here. # # Ten cases run on this arm: one lddtree smoke test, one scanelf symbol scan, # and the eight source-hygiene checks (typos, obsolete functions, obsolete # headers, PATH_MAX, the x* helpers, style, trailing space, and the elf.h # non-GNU regression #507470 was filed for). That is upstream's whole suite less # the python arm, and 10/10 was measured on an x86-64 native oracle at this # version with this same run-ptest before any wasm32 build was spent. # # --------------------------------------------------------------------------- # THE FACT THIS WHOLE PORT TURNS ON: THIS IMAGE HAS NO ELF BINARIES. # # Every executable here is a wasm module. pax-utils is an ELF toolchain utility, # so on this arm its subject cannot be anything the image ships -- it has to be # supplied. The ptest payload carries one: the x86-64 build of scanelf from THIS # RECIPE'S OWN -native variant, gzipped (a bare foreign ELF would be a wrong-arch # binary to the QA arch check and a thing to strip to the stripper, and stripping # it would delete the symbol table that IS the subject). run-ptest expands it # into the scratch build directory, which is exactly where upstream's # tests/scanelf/dotest looks for ${top_builddir}/scanelf. # # That premise is ASSERTED BELOW rather than assumed -- pax-utils/no-elf-here # scans a shipped binary and requires the scan to come back empty. If that case # ever goes red the image has grown an ELF and this file's reasoning needs # re-reading, which is precisely what one wants to be told. # # --------------------------------------------------------------------------- # THE ONE KNOWN NON-PASS, AND WHY IT IS NOT A DEFECT # # tests/lddtree/dotest-sfx runs `lddtree.sh /bin/bash` and requires that no # output line matches /none/i -- i.e. that bash resolves an ELF interpreter. On # this image /bin/bash is a wasm module and the whole userland is statically # linked, so lddtree correctly answers `interpreter => none` and upstream's # assertion is false by construction. Nothing is broken: the tool gives the right # answer, and the question is one this arm cannot be asked. It is carried below # as a named known non-pass, exactly as 83-mcpp.test carries its two. # # A skip that says "this cannot pass here" is worth nothing without something # that proves the tool still works, so the case has a POSITIVE CONTROL beside it: # pax-utils/lddtree-elf runs the SAME lddtree.sh, with the same grep, against the # ELF fixture -- and there it must come back empty, because a real ELF does # resolve its interpreter. A lddtree that genuinely broke fails that case and # cannot hide behind the skip. # # THE PYTHON ARM IS OFF through upstream's own knob. tests/lddtree/Makefile gates # two of its three cases behind `ifneq ($(USE_PYTHON),no)` because they run # lddtree.py and diff it against lddtree.sh; python3 is not built for this image # (BRINTOS-101 gates that chain), so run-ptest passes USE_PYTHON=no and both are # reported as skips below rather than left as a silent absence. ptestdir=/usr/lib/pax-utils/ptest runner=/usr/bin/ptest-runner # Upstream 1.3.3 declares three arms under tests/, and with USE_PYTHON=no they # contribute 1 + 1 + 8 = 10 cases. want_arms=3 want_total=10 want_unexpected_fail=0 want_known_nonpass=1 # Below this file's BST-TIMEOUT on purpose, so an overrun is reported by # ptest-runner in its own voice instead of this whole file being killed around # it -- the arrangement 77-nghttp2.test and 83-flatcc.test use. The margin is # what it is because THE STRUCTURAL CASES ABOVE RUN FIRST and spend from the same # bound: sizing this at the file's own 1200 once cost a `suite-timeout` after 30 # cases, with the runner still going and nothing wrong with it. runner_t=480 require scanelf pax-utils/present tmp=${TMPDIR:-/tmp}/bst-pax-utils.$$ mkdir -p "$tmp" 2>/dev/null || tmp=/tmp # ----------------------------------------------------------- what shipped ---- # `test -f`, never `test -x`. The shell's access check consults supplementary # groups and getgroups(2) is ENOSYS on this arm (brintos/linux#19), so # `[ -x ]` prints "sh: getgroups: Function not implemented" and then # answers TRUE whatever the mode is. What proves these binaries are executable is # that they are executed, below and by upstream's own suite. check_rc pax-utils/scanelf-bin 0 test -f /usr/bin/scanelf check_rc pax-utils/dumpelf-bin 0 test -f /usr/bin/dumpelf check_rc pax-utils/pspax-bin 0 test -f /usr/bin/pspax check_rc pax-utils/scanmacho-bin 0 test -f /usr/bin/scanmacho check_rc pax-utils/lddtree-bin 0 test -f /usr/bin/lddtree # lddtree is bin_SCRIPTS, and WHICH script it is depends on how the recipe # configured: upstream's Makefile.am copies lddtree.py when --with-python was # given and lddtree.sh otherwise. This image has no python3, so the shell one is # the only one that could run at all -- asserted so a PACKAGECONFIG change that # installed the python one would be a named failure rather than a `lddtree` that # reports "not found" on first use. check_out pax-utils/lddtree-is-sh "/bin/bash" head -n 1 /usr/bin/lddtree check_out pax-utils/lddtree-help "Display ELF dependencies as a tree" \ /usr/bin/lddtree -h check_out pax-utils/version "pax-utils-v1.3.3" scanelf -V check_out pax-utils/dumpelf-version "pax-utils-v1.3.3" dumpelf -V # ------------------------------------------------------- the ptest payload --- check_rc pax-utils/ptest-dir 0 test -d "$ptestdir" check_rc pax-utils/ptest-script 0 test -f "$ptestdir/run-ptest" check_rc pax-utils/ptest-lib 0 test -f "$ptestdir/src/tests/lib.sh" check_rc pax-utils/ptest-fixture 0 test -s "$ptestdir/scanelf-elf.gz" # The source tree the `source` arm lints is data, not scaffolding: dropping any # of it would silently narrow what those eight cases assert. Three files stand # for it here -- the one every arm needs, the one parsed to BUILD one of the # greps, and the one whose own case exists for a specific upstream regression. check_rc pax-utils/src-xfuncs 0 test -s "$ptestdir/src/xfuncs.h" check_rc pax-utils/src-elf-h 0 test -s "$ptestdir/src/elf.h" check_rc pax-utils/src-scanelf-c 0 test -s "$ptestdir/src/scanelf.c" # The tool the suite runs must be the tool the package ships. lib.sh prepends # ${top_srcdir} to PATH, so src/scanelf is what a bare `scanelf` resolves to # inside the suite; if it were absent the search would fall through to # ${top_builddir}, where the ELF fixture lives. check_rc pax-utils/src-tool-link 0 test -f "$ptestdir/src/scanelf" # What install counted out of upstream's own $(wildcard */Makefile), read before # the run so a payload that lost an arm is named as such rather than showing up # as a short total. staged_arms=$(cat "$ptestdir/arms.count" 2>/dev/null) check_eq pax-utils/ptest-arms "$want_arms" "$staged_arms" # BRINTOS-WORKAROUND: linux-in-the-browser#150 remove — the batching wrapper the # source arm cannot run without on this arm. xargs asks the system how large an # argv it may build and is told 128934 bytes against a real __wasm32_args_buf of # 8192, so every batch it builds is rejected -- and the rejection never reaches # the caller, so the pipeline hangs rather than failing (32 min observed). # Asserted here so the workaround is visible on target and its removal is a # decision rather than a silence; delete this case with the wrapper. `test -f`, # not `test -x`, for the reason given at the top of this section -- and what # proves the wrapper is executable is that the source arm passes at all. check_rc pax-utils/argmax-wrapper 0 test -f "$ptestdir/bin/xargs" # ------------------------------------------- the arm's premise, asserted ----- # scanelf against a shipped binary. Empty output is the RIGHT answer and the # point: /bin/bash here is a wasm module, so there is no ELF to find a symbol in. # This is what makes the ELF fixture necessary rather than convenient. noelf=$(scanelf -qsmain -F'%s' /bin/bash 2>/dev/null) check_eq pax-utils/no-elf-here "" "$noelf" # ------------------------------------------- the shipped tools, on an ELF ---- # Beyond upstream's suite, which only ever scans one file. The fixture is # expanded here into our own scratch: run-ptest's copy lives and dies inside its # own run. elf=$tmp/scanelf if gzip -dc "$ptestdir/scanelf-elf.gz" > "$elf" 2>/dev/null && [ -s "$elf" ]; then bst_ok pax-utils/fixture-expand # The symbol table read, which is upstream's own assertion (tests/scanelf) # stated here in its own right so a red names the tool rather than the arm. check_out pax-utils/scan-main "main" scanelf -qsmain -F'%s' "$elf" # The two program-header reads lddtree is built on: PT_INTERP and DT_NEEDED. # Both are x86-64 facts recovered by a wasm32 binary, which is the whole of # what a cross-ELF scanner claims to do. check_out pax-utils/scan-interp "/lib64/ld-linux-x86-64.so.2" \ scanelf -qF '#F%i' "$elf" check_out pax-utils/scan-needed "libc.so.6" scanelf -qF '#F%n' "$elf" # dumpelf writes the ELF back out as C declarations; the 64-bit header type is # the one word that says it read the class correctly. check_out pax-utils/dumpelf-ehdr "Elf64_Ehdr" dumpelf "$elf" # THE POSITIVE CONTROL for the known non-pass below: upstream's own lddtree # assertion, against a subject that can satisfy it. A resolvable interpreter # means no line matching /none/i. lt=$("$ptestdir/src/lddtree.sh" "$elf" 2>/dev/null | grep -i 'none') check_eq pax-utils/lddtree-elf "" "$lt" # scanmacho given an ELF must find no Mach-O in it. Cheap, and it is the only # statement this image can make about the fourth binary: it ships no Mach-O. check_rc pax-utils/scanmacho-runs 0 scanmacho -q "$elf" else bst_notok pax-utils/fixture-expand "cannot expand $ptestdir/scanelf-elf.gz" for c in scan-main scan-interp scan-needed dumpelf-ehdr lddtree-elf scanmacho-runs; do bst_notok "pax-utils/$c" "no ELF fixture to scan" done fi # pspax reads /proc rather than a file, so it is the one tool here whose subject # this image does have. The header is printed before the walk, so this passes on # a machine with no other process and fails if pspax cannot start. check_out pax-utils/pspax-header "USER" pspax # ------------------------------------------- the python arm, said out loud --- bst_skip pax-utils/lddtree.py.smoke \ "upstream's tests/lddtree runs lddtree.py and diffs it against lddtree.sh; python3 is not built for this image (BRINTOS-101), so run-ptest passes upstream's own USE_PYTHON=no" bst_skip pax-utils/lddtree-cmp \ "the py-vs-sh comparison cases (lddtree -l, lddtree -l -a) need the same absent python3" # ------------------------------------------------------- run it, once -------- # The harness is preferred wherever it exists, because this ticket closes on the # suite run UNDER ptest-runner -- its discovery and its tally are part of what is # asserted. The fallback runs the identical script directly and SKIPS only the # cases that are about the harness itself. out=$tmp/runner.out if [ -x "$runner" ]; then bst_ok pax-utils/ptest-runner-present "$runner" -t "$runner_t" pax-utils > "$out" 2>&1 rc=$? check_eq pax-utils/ptest-runner-rc 0 "$rc" begin=; end=; rerr= while read -r k v; do case "$k" in BEGIN:) begin=$v ;; END:) end=$v ;; ERROR:) rerr="$v" ;; esac done < "$out" check_eq pax-utils/runner-begin "$ptestdir" "$begin" check_eq pax-utils/runner-end "$ptestdir" "$end" check_eq pax-utils/runner-no-error "" "$rerr" else bst_skip pax-utils/ptest-runner-present "this image ships no ptest-runner" bst_skip pax-utils/ptest-runner-rc "no ptest-runner to run" for c in runner-begin runner-end runner-no-error; do bst_skip "pax-utils/$c" "harness cases have no subject without ptest-runner" done ( cd "$ptestdir" && sh ./run-ptest ) > "$out" 2>&1 fi # ------------------------------------- one case per upstream test ------------ npass=0 nfail=0 ntally=0 xfail_hit=0 xfail_pass=0 # The known non-pass, by exact case name. See the block at the top of this file # for why it cannot pass on an image with no ELF, and pax-utils/lddtree-elf above # for the control that keeps the skip honest. Unlike 83-mcpp.test's two, this one # does NOT reproduce on the x86-64 native oracle -- it passes there, because # there /bin/bash is an ELF. The arm is the whole of the difference. is_xfail() { case "$1" in "lddtree.sh.smoke") return 0 ;; *) return 1 ;; esac } while read -r verdict name rest; do case "$verdict" in PASS:) if is_xfail "$name"; then bst_notok "pax-utils/$name" "expected non-pass now passes -- either this image grew an ELF /bin/bash or the known-non-pass list in this file is stale" xfail_pass=$((xfail_pass + 1)) else bst_ok "pax-utils/$name" fi npass=$((npass + 1)) ;; FAIL:) if is_xfail "$name"; then bst_skip "pax-utils/$name" "lddtree.sh /bin/bash correctly reports 'interpreter => none' because /bin/bash is a wasm module and this userland is static; upstream's smoke case greps for exactly that word, so it is false by construction on this arm -- see pax-utils/lddtree-elf for the same assertion against a real ELF" xfail_hit=$((xfail_hit + 1)) else bst_notok "pax-utils/$name" "upstream assertion failed: $rest" fi nfail=$((nfail + 1)) ;; ERROR:) # run-ptest's own refusals (no payload, no fixture, unwritable scratch). # Distinct from an upstream assertion failing, and reported as such rather # than being read past on the way to a short total. bst_notok pax-utils/ptest-setup "run-ptest refused: $name $rest" ;; INFO:) # INFO: pax-utils-ptest tests=N pass=P fail=F make-rc=R (the per-failure # diagnostics carry a `|` in $name and are not a tally) case "$name" in pax-utils-ptest) for kv in $rest; do case "$kv" in tests=*) ntally=${kv#tests=} ;; esac done ;; esac ;; esac done < "$out" # The pinned total. Without it, a run that produced three PASS lines and died # would read as three green cases and nothing else -- the failure that hides best # is a suite that got shorter and still reported green. check_eq pax-utils/suite-total "$want_total" "$((npass + nfail))" # run-ptest's own tally against what it actually printed: they disagree only if # the console lost lines between the two, which is the truncation this catches. check_eq pax-utils/tally-agrees "$ntally" "$((npass + nfail))" # Every non-pass accounted for: the one known non-pass hit, nothing else red, and # nothing on the known list quietly passing. check_eq pax-utils/known-nonpass "$want_known_nonpass" "$xfail_hit" check_eq pax-utils/stale-nonpass 0 "$xfail_pass" check_eq pax-utils/unexpected-fail "$want_unexpected_fail" "$((nfail - xfail_hit))" rm -rf "$tmp"