. "$BST_LIB" # libjitterentropy is the CPU-jitter entropy source: it times a hash over its own # memory-access loop and harvests the variation in that timing as raw entropy, # conditioned through SHA3-256. It is what the kernel's jitterentropy_rng and # libgcrypt/OpenSSL/AWS-LC reach for when they need a seed with no hardware RNG. # # The distro is static-only, so what the image gets is /usr/lib/libjitterentropy.a # and two headers. There is NO executable in this package on this arm -- nothing # here is reachable from a shell, and a payload-only suite could assert only that # an archive and a header exist, which would pass just as happily if every entry # point in the archive trapped on first call. # # --------------------------------------------------------------------------- # WHY THIS SUITE IS A PROBE, AND WHY ITS EXPECTATIONS ARE SPLIT IN TWO # # data/libjitterentropy-probe is cross-linked with wasmcc-glibc against THE VERY # ARCHIVE THAT SHIPS (/usr/lib/libjitterentropy.a), staged to $TMPDIR and run # here. Its source is src/libjitterentropy_probe.c. # # This package is unusual on this Epic in that MOST of what it does cannot be # pinned to a native control, because its entire job is to measure the machine it # runs on. So the probe prints two kinds of line and this suite treats them # differently: # # JE-CASE -- an arch-neutral fact: return codes, lengths, version arithmetic, # structural properties of the output. Asserted literally; every # expected value is one the native x86_64 control produced. # JE-DIAG -- a MEASUREMENT of this machine (clock resolution, tick size, # distinct-sample count). Never asserted -- it is the evidence the # cases below are adjudicated against. # # THE STARTUP HEALTH TEST IS THE DIVIDING LINE. jent_entropy_init() runs # upstream's own gate: it refuses to hand out a collector at all unless this # machine's clock has enough resolution and variation to feed a jitter source # (ENOTIME / ECOARSETIME / ENOMONOTONIC / EMINVARIATION / ...). That verdict is a # property of the PLATFORM, not of the port, so: # # - init == 0 -> the entropy-generation cases run and are asserted. # - init != 0 -> those cases are SKIPPED against the documented refusal # code, and what is asserted instead is that the library # refused CORRECTLY -- a documented errno, not a wedge, not # a wild pointer, and not entropy handed out anyway. # # A skip here is the honest verb: the library is present and behaving exactly as # upstream specifies for a machine whose clock cannot feed it. It is NOT a broken # tool downgraded to keep the suite green -- a library that DID hand out a buffer # after refusing to initialise would be a `not ok`, and jent/init-refusal-coherent # below is the case that would catch it. # # --------------------------------------------------------------------------- # THE STACK PROTECTOR IS OFF, AND THAT IS WHAT MAKES THIS ARCHIVE LINKABLE # # Upstream hardens itself (`ENABLE_STACK_PROTECTOR ?= 1` in its Makefile), and the # arm's glibc defines no __stack_chk_fail, so the stock archive is unlinkable by # ANY consumer -- wasmld refuses the phantom env.__stack_chk_fail import. The # recipe therefore builds with ENABLE_STACK_PROTECTOR=0 # (BRINTOS-WORKAROUND: linux-in-the-browser#136). The probe existing at all is # what holds that in place: restore the hardening and this suite stops having a # payload to run, loudly, at build time rather than silently at run time. incd=/usr/include lib=/usr/lib/libjitterentropy.a hasfile() { if [ -s "$2" ]; then bst_ok "$1"; else bst_notok "$1" "absent or empty: $2"; fi; } has() { case "$3" in *"$2"*) bst_ok "$1" ;; *) bst_notok "$1" "not found: $2" ;; esac; } inar() { if grep -qa -- "$2" "$lib" 2>/dev/null; then bst_ok "$1"; else bst_notok "$1" "not in archive: $2"; fi; } # --- the payload ------------------------------------------------------------ # The archive and the two headers are the whole shipped surface, so their absence # is reported as its own case rather than as a probe that would not have linked. hasfile jent/lib-archive "$lib" hasfile jent/header-jitterentropy "$incd/jitterentropy.h" hasfile jent/header-base-user "$incd/jitterentropy-base-user.h" # A static-only distro must ship no shared object. Upstream's `install` target is # install-man + install-shared + install-includes -- install-static is NOT in it # -- so a recipe that took upstream's default would install the one artifact this # arm cannot build and omit the one it ships. This case is what says which way # round it landed. set -- /usr/lib/libjitterentropy.so* if [ -e "$1" ]; then bst_notok jent/no-shared-object "shared object present: $1" else bst_ok jent/no-shared-object; fi hdr=$(cat "$incd/jitterentropy.h" 2>/dev/null) # The four entry points every consumer names. A header that packaged without them # is a broken -dev, however good the archive behind it is. has jent/header-decl-read 'ssize_t jent_read_entropy(' "$hdr" has jent/header-decl-read-safe 'ssize_t jent_read_entropy_safe(' "$hdr" has jent/header-decl-alloc 'struct rand_data *jent_entropy_collector_alloc(' "$hdr" has jent/header-decl-free 'void jent_entropy_collector_free(' "$hdr" # The two flags the probe's refusal case drives, and the conditioner's digest # size -- release constants, so they are asserted literally. has jent/header-flag-force-timer '#define JENT_FORCE_INTERNAL_TIMER (1<<3)' "$hdr" has jent/header-flag-disable-timer '#define JENT_DISABLE_INTERNAL_TIMER (1<<4)' "$hdr" has jent/header-sha3-digest-bits '#define SHA3_256_SIZE_DIGEST_BITS 256' "$hdr" has jent/header-apt-window '#define JENT_APT_WINDOW_SIZE 512' "$hdr" # THE ARCHIVE'S CONTENTS, WITHOUT BINUTILS. This image ships no `ar` and no `nm`, # and reaching for them is not merely unavailable but wrong here: host binutils # cannot read these members either -- every one is LLVM bitcode, because -flto # compile output on this arm is IR rather than an ELF object. So the names are # asserted as BYTES in the archive: ar's member headers and the archive symbol # index both store plain ASCII, and grep is on the image. What that cannot show -- # that a name is DEFINED rather than merely referenced -- is exactly what the # probe below shows, by linking against this same archive and calling through it. # # All six members matter and none is optional: noise is the collector loop, timer # the clock abstraction (and the internal-timer thread), health the SP800-90B APT # and RCT tests, sha3 the conditioner, gcd the common-divisor screen, base the # public API. An archive that lost health or sha3 would still link a consumer. inar jent/archive-base jitterentropy-base.o inar jent/archive-gcd jitterentropy-gcd.o inar jent/archive-health jitterentropy-health.o inar jent/archive-noise jitterentropy-noise.o inar jent/archive-sha3 jitterentropy-sha3.o inar jent/archive-timer jitterentropy-timer.o # The symbols a consumer links. inar jent/symbol-init jent_entropy_init inar jent/symbol-init-ex jent_entropy_init_ex inar jent/symbol-alloc jent_entropy_collector_alloc inar jent/symbol-free jent_entropy_collector_free inar jent/symbol-read jent_read_entropy inar jent/symbol-read-safe jent_read_entropy_safe inar jent/symbol-version jent_version # --- the library RUNNING, via the cross-linked probe ------------------------- # Run under `timeout`: the probe's LAST stage forces the internal-timer path, # which spawns a thread and joins it. On an arm where thread join does not # complete that is where a trace stops, and a wedged probe would otherwise take # the whole runner down with it. Everything the probe has printed by then is # already on stdout, so a timeout still yields the finding rather than nothing. probe=${TMPDIR:-/tmp}/bst-jent-probe.$$ if cp "$BST_DATA/libjitterentropy-probe" "$probe" 2>/dev/null && chmod +x "$probe" 2>/dev/null; then if command -v timeout >/dev/null 2>&1; then out=$(timeout 120 "$probe" 2>&1); rc=$? else out=$("$probe" 2>&1); rc=$? fi rm -f "$probe" # The probe prints JE-END as its last line. Asserting it separately from the # exit status is what distinguishes "ran to completion and something failed" # from "stopped mid-trace" -- two very different findings that a bare rc # collapses into one. has jent/run-reached-end "JE-END" "$out" # Identity, from the ARCHIVE at run time rather than from the header: a -dev # packaged from a different build than its archive fails exactly this case. has jent/run-version "JE-CASE ok version-3.4.1" "$out" # The clock underneath everything. jent cannot work at all on a clock that does # not advance between back-to-back reads, so these two are the root capability # and are asserted, not measured away. has jent/run-timer-advances "JE-CASE ok timer-advances" "$out" has jent/run-timer-monotonic "JE-CASE ok timer-monotonic" "$out" # Arch-neutral parameter check: the allocator refuses FORCE|DISABLE internal # timer together, ahead of any timing work, so it holds whatever the clock does. has jent/run-flag-conflict-refused "JE-CASE ok conflicting-timer-flags-refused" "$out" # THE DIVIDING LINE: upstream's startup health test. initrc=$(printf '%s\n' "$out" | sed -n 's/^JE-INIT \(-*[0-9][0-9]*\) .*$/\1/p' | head -1) initnm=$(printf '%s\n' "$out" | sed -n 's/^JE-INIT -*[0-9][0-9]* \(.*\)$/\1/p' | head -1) res=$(printf '%s\n' "$out" | sed -n 's/^JE-DIAG timer-getres-ns \(.*\)$/\1/p' | head -1) tick=$(printf '%s\n' "$out" | sed -n 's/^JE-DIAG timer-tick-ns \(.*\)$/\1/p' | head -1) if [ -z "$initrc" ]; then bst_notok jent/run-init-reported "the probe printed no JE-INIT line: $(_bst_1line "$out")" why="no JE-INIT line" initrc=missing elif [ "$initrc" = 0 ]; then bst_ok jent/run-init-reported why="" else bst_ok jent/run-init-reported why="jent_entropy_init: $initnm ($initrc); clock getres=${res:-?}ns tick=${tick:-?}ns" fi # An UNKNOWN code means the library returned something outside its own # documented set -- a real defect, whichever way the health test went. if [ "$initnm" = UNKNOWN ]; then bst_notok jent/run-init-code-documented "undocumented jent_entropy_init return: $initrc" else bst_ok jent/run-init-code-documented fi if [ "$initrc" = 0 ]; then # The collector surface, against a machine upstream accepted. has jent/run-collector-alloc "JE-CASE ok collector-alloc" "$out" has jent/run-read-full-length "JE-CASE ok read-full-length" "$out" has jent/run-read-not-constant "JE-CASE ok read-not-constant" "$out" has jent/run-read-again-full "JE-CASE ok read-again-full" "$out" has jent/run-read-differs "JE-CASE ok read-differs-from-first" "$out" has jent/run-collector-free "JE-CASE ok collector-free" "$out" has jent/run-read-safe-full "JE-CASE ok read-safe-full-length" "$out" bst_ok jent/init-refusal-coherent else for c in run-collector-alloc run-read-full-length run-read-not-constant \ run-read-again-full run-read-differs run-collector-free \ run-read-safe-full; do bst_skip "jent/$c" "$why" done # THE CASE THAT KEEPS THE SKIPS HONEST. Having refused to initialise, the # library must not then have handed out a collector or a buffer anyway. The # probe prints JE-SKIP for that whole branch and no JE-READ/JE-ALLOC line, so # a stray one here means the refusal was not real. case "$out" in *"JE-ALLOC"*|*"JE-READ "*|*"JE-SAFE "*) bst_notok jent/init-refusal-coherent \ "init refused ($initnm) but the collector surface still produced output" ;; *"JE-SKIP collector-surface"*) bst_ok jent/init-refusal-coherent ;; *) bst_notok jent/init-refusal-coherent \ "init refused ($initnm) with neither a JE-SKIP marker nor collector output" ;; esac fi # The probe's own tally: whatever set of cases this machine ran, all of them # passed. This is arch-neutral BECAUSE the total is not pinned -- it is the one # assertion that stays true and stays meaningful on either side of the divide. has jent/run-exit-clean "JE-EXIT 0" "$out" # The internal-timer fallback is a DIAGNOSTIC, not a case: it spawns a thread, # and thread-join behaviour on this arm is its own open question. What is # asserted is only that the probe survived the attempt -- which is exactly what # jent/run-reached-end above already says -- so a wedge shows up as a missing # JE-END and a non-zero rc rather than as a silently absent case. if [ "$rc" -ne 0 ] && [ "$rc" -ne 1 ]; then bst_notok jent/run-probe-status "probe rc=$rc (124 = timed out, likely the internal-timer thread join)" else bst_ok jent/run-probe-status fi else bst_notok jent/probe-staged "cannot stage $BST_DATA/libjitterentropy-probe" fi