#!/bin/sh set -eu : "${PRE15_DUT:?PRE15_DUT is required}" : "${PRE15_ROOT:?PRE15_ROOT is required}" : "${PRE15_CASE_TMP:?PRE15_CASE_TMP is required}" : "${PRE15_RUN_DIR:?PRE15_RUN_DIR is required}" : "${PRE15_LIB_DIR:?PRE15_LIB_DIR is required}" . "$PRE15_LIB_DIR/runner.sh" baseline=93d0c64e321f50cf68026c3f1c0c95293bf17302 case_script=$PRE15_DUT/tests/pre15/cases/B33-cache-policy.sh fixture_dir=$PRE15_DUT/tests/pre15/fixtures gate_dir=$PRE15_DUT/tests/pre15/gates oracle=$fixture_dir/B33-cache-oracle.c qemu_spec=$fixture_dir/B33-cache-state.json qemu_generator=$fixture_dir/B33-cache-generate.py qemu_runner=$fixture_dir/B33-qemu-run.sh b32_probe=$fixture_dir/B32-cache-probe.c kld_builder=$fixture_dir/B28-build-kld.sh gate_input=$gate_dir/P15-038-input.json gate_result=$PRE15_ROOT/planning/pre15/evidence/20260816T021736Z-G05-P15-038/result.json gate_benefit=$PRE15_ROOT/planning/pre15/evidence/20260816T021736Z-G05-P15-038/benefit.json gate_state=$PRE15_ROOT/planning/pre15/evidence/20260816T021736Z-G05-P15-038/state-model.json gate_hashes=$PRE15_ROOT/planning/pre15/evidence/20260816T021736Z-G05-P15-038/SHA256SUMS artifacts=$PRE15_RUN_DIR/artifacts for tool in cc git python3 sha256sum timeout; do command -v "$tool" >/dev/null 2>&1 || \ pre15_infra_blocked "missing B33 host tool: $tool" done for fixture in "$case_script" "$oracle" "$qemu_spec" "$qemu_generator" "$qemu_runner" \ "$b32_probe" "$kld_builder" "$gate_input" "$gate_result" \ "$gate_benefit" "$gate_state" "$gate_hashes"; do pre15_record_fixture "b33-$(basename "$fixture")" "$fixture" done for source in internal.h zdata.c; do pre15_record_fixture "b33-$source" "$PRE15_DUT/src/$source" done mkdir -p "$artifacts" if test "${PRE15_QEMU_TARGET_ONLY:-0}" = 1; then pre15_target_reached else if ! python3 -B - "$PRE15_ROOT" "$PRE15_DUT" "$baseline" "$gate_input" \ "$gate_result" "$gate_benefit" "$gate_state" \ "$artifacts/B33-source-audit.json" <<'PY' from __future__ import annotations import json from pathlib import Path import re import subprocess import sys root = Path(sys.argv[1]) dut = Path(sys.argv[2]) baseline = sys.argv[3] implementation = "bc56f830918b76029871b60cca2e53992de70a2e" current_owners = { "internal.h": "aa20623132b6821c6ad4651b263fc0ac0f7a14c8", "zdata.c": implementation, } gate_input = json.loads(Path(sys.argv[4]).read_text(encoding="ascii")) gate_result = json.loads(Path(sys.argv[5]).read_text(encoding="ascii")) gate_benefit = json.loads(Path(sys.argv[6]).read_text(encoding="ascii")) gate_state = json.loads(Path(sys.argv[7]).read_text(encoding="ascii")) report_path = Path(sys.argv[8]) def fail(message: str) -> None: raise SystemExit(message) def committed(commit: str, relative: str) -> str: completed = subprocess.run( ["git", "-C", str(root), "show", f"{commit}:{relative}"], check=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, timeout=30, ) if completed.returncode != 0: fail(f"cannot read B33 source {commit}:{relative}: {completed.stderr.strip()}") return completed.stdout def function(source: str, name: str) -> str: match = re.search(rf"\n(?:static\s+)?[^\n]+\n{name}\([^{{]+\n\{{", source) if match is None: fail(f"cannot locate function {name}") start = match.start() + 1 brace = source.find("{", match.start()) depth = 0 for index in range(brace, len(source)): if source[index] == "{": depth += 1 elif source[index] == "}": depth -= 1 if depth == 0: return source[start : index + 1] fail(f"unterminated function {name}") expected_changed = { "repo-pre-15/src/internal.h", "repo-pre-15/src/zdata.c", "repo-pre-15/tests/pre15/cases/B33-cache-policy.sh", "repo-pre-15/tests/pre15/fixtures/B33-cache-generate.py", "repo-pre-15/tests/pre15/fixtures/B33-cache-oracle.c", "repo-pre-15/tests/pre15/fixtures/B33-cache-state.json", "repo-pre-15/tests/pre15/fixtures/B33-qemu-run.sh", } changed = subprocess.run( [ "git", "-C", str(root), "diff-tree", "--no-commit-id", "--name-only", "-r", implementation, ], check=True, stdout=subprocess.PIPE, text=True, timeout=30, ).stdout.splitlines() if set(changed) != expected_changed or len(changed) != len(expected_changed): fail(f"B33 implementation write set mismatch: {changed!r}") for name, commit in (("baseline", baseline), ("implementation", implementation)): if subprocess.check_output( ["git", "-C", str(root), "rev-parse", commit], text=True ).strip() != commit: fail(f"B33 {name} identity changed") internal = (dut / "src/internal.h").read_text(encoding="utf-8") zdata = (dut / "src/zdata.c").read_text(encoding="utf-8") implemented_internal = committed(implementation, "repo-pre-15/src/internal.h") implemented_zdata = committed(implementation, "repo-pre-15/src/zdata.c") for name, source in (("internal.h", internal), ("zdata.c", zdata)): owner = current_owners[name] if source != committed(owner, f"repo-pre-15/src/{name}"): fail(f"current B33 source differs from accepted owner {owner}: {name}") baseline_zdata = committed(baseline, "repo-pre-15/src/zdata.c") for preserved in ("z_erofs_decode_length", "z_erofs_decode_extent"): if function(implemented_zdata, preserved) != function(baseline_zdata, preserved): fail(f"B25/B27/B28 read/decode path changed in B33: {preserved}") do_read = function(implemented_zdata, "z_erofs_do_read") baseline_do_read = function(baseline_zdata, "z_erofs_do_read") bypass_delta = ( "\t\t\t} else\n" "\t\t\t\tz_erofs_extent_cache_record_bypass(sbi, &map);" ) if do_read.count(bypass_delta) != 1 or do_read.replace(bypass_delta, "\t\t\t}") != baseline_do_read: fail("B33 z_erofs_do_read differs beyond policy-rejection accounting") required_b32 = ( "cache->nid == vi->nid", "cache->decoded_size == decoded_size", "cache->map.m_pa == map->m_pa", "cache->map.m_la == map->m_la", "cache->map.m_plen == map->m_plen", "cache->map.m_llen == map->m_llen", "cache->map.m_deviceid == map->m_deviceid", "cache->map.m_algorithmformat == map->m_algorithmformat", "cache->map.m_flags == map->m_flags", "cache->state == EROFS_ZCACHE_INFLIGHT || cache->waiters != 0", "cv_wait(&cache->cv, &sbi->z_extent_cache_lock)", "cache->error = error", "cache->closing = true", "Z_EROFS_CACHE_BYPASS", "free(decoded, M_EROFS);", ) missing = [token for token in required_b32 if token not in zdata] if missing: fail(f"B32 key/inflight/failure/fallback token missing: {missing!r}") policy = function(zdata, "z_erofs_extent_cache_eligible") for named_codec in ( "Z_EROFS_COMPRESSION_LZ4", "Z_EROFS_COMPRESSION_LZMA", "Z_EROFS_COMPRESSION_DEFLATE", "Z_EROFS_COMPRESSION_ZSTD", ): if named_codec in policy: fail(f"B33 admission still names a codec: {named_codec}") for token in ( "map->m_algorithmformat < Z_EROFS_COMPRESSION_MAX", "len <= cache->budget_bytes", "decode_work >= cache->minimum_decode_work", "z_erofs_cache_enabled != 0", ): if token not in policy: fail(f"codec-neutral work/size policy token is absent: {token}") if "map->m_algorithmformat == Z_EROFS_COMPRESSION_LZMA" in zdata: fail("LZMA-only admission branch remains") record_bypass = function(zdata, "z_erofs_extent_cache_record_bypass") if "++cache->metrics[map->m_algorithmformat].bypasses" not in record_bypass or ( "z_erofs_extent_cache_record_bypass(sbi, &map)" not in do_read ): fail("B33 policy rejection is absent from four-codec accounting") budget = gate_input["budget"] expected_macros = { "EROFS_ZCACHE_MOUNT_HARD_BUDGET": budget["per_mount_bytes"] // 1024, "EROFS_ZCACHE_GLOBAL_HARD_BUDGET": budget["global_bytes"] // 1024, "EROFS_ZCACHE_MIN_DECODE_WORK": budget["minimum_decode_work_bytes"] // 1024, } for name, kib in expected_macros.items(): if f"#define {name} ({kib}UL * 1024)" not in zdata: fail(f"B33 hard policy differs from G05: {name}") for token in ( 'TUNABLE_INT("vfs.erofs.decoded_cache.enabled"', 'TUNABLE_ULONG("vfs.erofs.decoded_cache.mount_budget"', 'TUNABLE_ULONG("vfs.erofs.decoded_cache.global_budget"', 'TUNABLE_ULONG("vfs.erofs.decoded_cache.minimum_decode_work"', "MIN(z_erofs_cache_mount_budget,", "MIN(z_erofs_cache_global_budget,", "EVENTHANDLER_REGISTER(vm_lowmem", "EVENTHANDLER_DEREGISTER(vm_lowmem", "LIST_FOREACH(sbi, &z_erofs_cache_mounts, z_extent_cache_link)", "mtx_trylock(&sbi->z_extent_cache_lock)", ): if token not in zdata: fail(f"B33 budget/config/reclaim token is absent: {token}") claim = function(zdata, "z_erofs_extent_cache_claim") if claim.find("z_erofs_extent_cache_reserve(decoded_size)") > claim.find( "cache->state = EROFS_ZCACHE_INFLIGHT" ): fail("B33 does not reserve before inflight decode") drop_position = claim.find("z_erofs_extent_cache_drop_locked(cache, true, false)") empty_position = claim.find("cache->state = EROFS_ZCACHE_EMPTY", drop_position) reserve_position = claim.find("z_erofs_extent_cache_reserve(decoded_size)") if drop_position < 0 or not (drop_position < empty_position < reserve_position): fail("B33 replacement reservation failure can leave READY without data") complete = function(zdata, "z_erofs_extent_cache_complete") if complete.find("z_erofs_extent_cache_release(cache->charged_bytes)") > complete.find( "cache->state = EROFS_ZCACHE_FAILED" ): fail("B33 failed decode publishes before releasing reservation") for field in ( "hits", "misses", "bypasses", "evictions", "reclaims", "resident_bytes", "charged_bytes", "budget_bytes", "minimum_decode_work", "metrics[Z_EROFS_COMPRESSION_MAX]", ): if field not in internal: fail(f"B33 four-codec accounting field is absent: {field}") for forbidden in ("shrinker", "workqueue", "work_struct", "wait_on_bit", "folio"): if forbidden in internal + zdata: fail(f"Linux-only cache primitive introduced: {forbidden}") if gate_result.get("status") != "GO" or gate_result.get("b33") != "AUTHORIZED": fail("P15-038 G05 result is not GO") if gate_state.get("status") != "PASS" or gate_state["budget"]["global_remaining"] != 0: fail("P15-038 state model is not closed") if gate_benefit.get("codecs_meeting_threshold", 0) < 2 or any( len(summary["current_ns"]) != 5 or len(summary["candidate_ns"]) != 5 for summary in gate_benefit.get("summaries", []) ): fail("P15-038 fixed five-sample benefit is incomplete") report = { "baseline": baseline, "batch": "B33", "b32_contract_preserved": True, "codec_neutral": True, "current_owners": current_owners, "gate_codecs_meeting_threshold": gate_benefit["codecs_meeting_threshold"], "global_hard_budget": budget["global_bytes"], "implementation": implementation, "lock_order": ["global-list", "mount-cache", "global-budget"], "mount_hard_budget": budget["per_mount_bytes"], "no_cache_fallback": True, "reclaim": "FreeBSD-vm_lowmem", "write_set": sorted(changed), } report_path.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="ascii") print(json.dumps(report, indent=2, sort_keys=True)) PY then pre15_runner_fail 'B33 source, gate, write-set, or preservation audit failed' fi if ! (cd "$PRE15_ROOT/planning/pre15/evidence/20260816T021736Z-G05-P15-038" && \ timeout -k 5 30 sha256sum -c SHA256SUMS) \ >"$artifacts/gate-manifest.stdout" 2>"$artifacts/gate-manifest.stderr"; then pre15_runner_fail 'P15-038 gate evidence manifest failed' fi if ! cc -std=c11 -O2 -Wall -Wextra -Werror "$oracle" \ -o "$PRE15_CASE_TMP/B33-cache-oracle" \ >"$artifacts/oracle-build.stdout" 2>"$artifacts/oracle-build.stderr"; then pre15_runner_fail 'B33 cache budget oracle did not compile' fi if ! timeout -k 5 60 "$PRE15_CASE_TMP/B33-cache-oracle" \ >"$artifacts/oracle.json" 2>"$artifacts/oracle.stderr"; then pre15_dut_fail 'B33 cache budget/accounting oracle failed' fi if ! python3 -B - "$artifacts/oracle.json" <<'PY' import json from pathlib import Path import sys result = json.loads(Path(sys.argv[1]).read_text(encoding="ascii")) if result.get("status") != "PASS": raise SystemExit("B33 oracle status is not PASS") if result.get("global_peak") != result.get("global_limit") or result.get("global_remaining") != 0: raise SystemExit("B33 oracle global budget did not close") metrics = result.get("metrics", []) if len(metrics) != 4 or [item.get("codec") for item in metrics] != list(range(4)): raise SystemExit("B33 four-codec metric set is incomplete") for item in metrics: if item.get("hits", 0) < 1 or item.get("misses", 0) < 1 or item.get("evictions", 0) < 1: raise SystemExit(f"B33 codec metric is incomplete: {item!r}") if item.get("resident_bytes") != 0: raise SystemExit(f"B33 codec resident bytes leaked: {item!r}") PY then pre15_dut_fail 'B33 four-codec accounting result is incomplete' fi sha256sum "$artifacts/B33-source-audit.json" "$artifacts/oracle.json" \ "$artifacts/gate-manifest.stdout" >"$artifacts/SHA256SUMS" fi pre15_target_reached if test "${PRE15_MODE:-host}" != qemu; then printf '%s\n' \ 'B33-cache-budget host PASS' \ 'Four-codec admission/accounting, mount/global hard budgets, exhaustion fallback, reclaim, failure retry, disable, and eviction hash PASS' \ 'P15-038 host codec-cost gate replay manifest PASS; no guest vnode performance claimed' \ 'TC084/TC105/TC129 NOT_RUN (not necessary)' \ 'QEMU TC168, TC130, and full feature suite NOT_RUN in host mode' exit 0 fi test "${PRE15_B32_SCENARIO:-}" = TC168-cache-inflight || \ pre15_runner_fail 'B33 QEMU mode requires TC168-cache-inflight' for tool in clang cmp diff file mkfs.erofs nm scp tar timeout; do command -v "$tool" >/dev/null 2>&1 || \ pre15_infra_blocked "missing B33 QEMU tool: $tool" done : "${PRE15_QEMU_CONTROL_PATH:?QEMU control path is required}" : "${PRE15_QEMU_SSH_KEY:?QEMU SSH key is required}" : "${PRE15_QEMU_SSH_PORT:?QEMU SSH port is required}" : "${PRE15_QEMU_SSH_USER:?QEMU SSH user is required}" first=$PRE15_CASE_TMP/fixtures-first second=$PRE15_CASE_TMP/fixtures-second if ! (umask 022 && timeout -k 5 180 python3 -B "$qemu_generator" \ --spec "$qemu_spec" --output "$first") \ >"$artifacts/generate-first.json" 2>"$artifacts/generate-first.stderr" || \ ! (umask 022 && timeout -k 5 180 python3 -B "$qemu_generator" \ --spec "$qemu_spec" --output "$second") \ >"$artifacts/generate-second.json" 2>"$artifacts/generate-second.stderr"; then pre15_runner_fail 'B33 fixture generation failed' fi if ! diff -qr "$first" "$second" >"$artifacts/fixture-repeat.diff"; then pre15_runner_fail 'B33 fixtures are not byte reproducible' fi cp "$first/fixture-index.json" "$artifacts/B33-fixture-index.json" module=$PRE15_CASE_TMP/B33-erofs-zstdio0.ko if ! timeout -k 10 600 /bin/sh "$kld_builder" "$PRE15_DUT" \ "$PRE15_FREEBSD_SRC" "$module" "$PRE15_CASE_TMP/kld-work" 0 \ >"$artifacts/kld-build.stdout" 2>"$artifacts/kld-build.stderr"; then pre15_dut_fail 'B33 cross-target zstdio0 KLD build failed' fi pre15_record_module "$module" file "$module" >"$artifacts/kld-file.txt" sha256sum "$module" >"$artifacts/kld-sha256.txt" nm -u "$module" | LC_ALL=C sort >"$artifacts/kld-nm-u.txt" fixture_archive=$PRE15_CASE_TMP/B33-fixtures.tar.gz tar -C "$first" -czf "$fixture_archive" . pre15_scp() { timeout -k 5 "${PRE15_GUEST_COMMAND_TIMEOUT:-60}" scp -O -q \ -o BatchMode=yes -o StrictHostKeyChecking=no \ -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 \ -o "ControlPath=$PRE15_QEMU_CONTROL_PATH" \ -i "$PRE15_QEMU_SSH_KEY" -P "$PRE15_QEMU_SSH_PORT" \ "$1" "$PRE15_QEMU_SSH_USER@127.0.0.1:$2" } pre15_guest_ssh_bounded() { timeout -k 5 "${PRE15_GUEST_COMMAND_TIMEOUT:-60}" ssh -n -q \ -o BatchMode=yes -o StrictHostKeyChecking=no \ -o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 \ -o "ControlPath=$PRE15_QEMU_CONTROL_PATH" \ -p "$PRE15_QEMU_SSH_PORT" \ "$PRE15_QEMU_SSH_USER@127.0.0.1" "$@" } if ! pre15_scp "$fixture_archive" /root/B33-fixtures.tar.gz || \ ! pre15_scp "$b32_probe" /root/B33-cache-probe.c || \ ! pre15_scp "$module" /root/B33-erofs-zstdio0.ko; then pre15_infra_blocked 'could not transfer B33 module, probe, or fixtures' fi if ! pre15_guest_ssh_bounded \ 'rm -rf /root/B33-fixtures && mkdir /root/B33-fixtures && tar -xzf /root/B33-fixtures.tar.gz -C /root/B33-fixtures && cc -O2 -Wall -Wextra -Werror -pthread -o /root/B33-cache-probe /root/B33-cache-probe.c'; then pre15_infra_blocked 'could not prepare B33 guest fixtures/probe' fi if pre15_guest_ssh_bounded kldstat -q -m erofs >/dev/null 2>&1; then pre15_infra_blocked 'guest kernel already owns the erofs.1 interface' fi module_sha256=$(sha256sum "$module" | awk '{print $1}') guest_module_sha256=$(pre15_guest_ssh_bounded sha256 -q /root/B33-erofs-zstdio0.ko) || \ pre15_infra_blocked 'could not hash transferred B33 KLD' printf 'host=%s guest=%s\n' "$module_sha256" "$guest_module_sha256" \ >"$artifacts/guest-module-sha256.txt" test "$guest_module_sha256" = "$module_sha256" || \ pre15_runner_fail 'transferred B33 KLD hash differs from host module' pre15_guest_ssh_bounded kldstat >"$artifacts/guest-kldstat-before-load.txt" pre15_guest_ssh_bounded dmesg >"$artifacts/guest-dmesg-before-load.txt" cache_tunable=vfs.erofs.decoded_cache.minimum_decode_work cache_tunable_before=$(pre15_guest_ssh_bounded kenv -q "$cache_tunable" || true) printf 'name=%s before=%s test-value=0\n' "$cache_tunable" \ "${cache_tunable_before:-unset}" >"$artifacts/cache-tunable.txt" if ! pre15_guest_ssh_bounded kenv "$cache_tunable=0" \ >"$artifacts/cache-tunable-set.stdout" \ 2>"$artifacts/cache-tunable-set.stderr"; then pre15_infra_blocked 'could not force B33 decoded-cache admission' fi if ! pre15_guest_ssh_bounded kldload /root/B33-erofs-zstdio0.ko \ >"$artifacts/kldload.stdout" 2>"$artifacts/kldload.stderr"; then if test -n "$cache_tunable_before"; then pre15_guest_ssh_bounded kenv "$cache_tunable=$cache_tunable_before" || true else pre15_guest_ssh_bounded kenv -u "$cache_tunable" || true fi if grep -q 'module already loaded or in kernel' \ "$artifacts/kldload.stderr"; then pre15_infra_blocked 'guest kernel already owns the erofs.1 interface' fi pre15_dut_fail 'B33 exact-source zstdio0 KLD failed to load' fi if test -n "$cache_tunable_before"; then pre15_guest_ssh_bounded kenv "$cache_tunable=$cache_tunable_before" \ >"$artifacts/cache-tunable-restore.stdout" \ 2>"$artifacts/cache-tunable-restore.stderr" || \ pre15_infra_blocked 'could not restore B33 cache tunable' else pre15_guest_ssh_bounded kenv -u "$cache_tunable" \ >"$artifacts/cache-tunable-restore.stdout" \ 2>"$artifacts/cache-tunable-restore.stderr" || \ pre15_infra_blocked 'could not remove B33 cache tunable override' fi cache_tunable_after=$(pre15_guest_ssh_bounded kenv -q "$cache_tunable" || true) printf 'after=%s\n' "${cache_tunable_after:-unset}" \ >>"$artifacts/cache-tunable.txt" if test "$cache_tunable_after" != "$cache_tunable_before"; then pre15_runner_fail 'B33 cache tunable was not restored exactly' fi if ! pre15_guest_ssh_bounded kldstat -q -m erofs; then pre15_dut_fail 'B33 kldload returned success without erofs.1 ownership' fi if ! pre15_guest_ssh_bounded kldstat -q -n B33-erofs-zstdio0.ko; then pre15_dut_fail 'B33 kldload returned success without exact KLD file ownership' fi pre15_guest_ssh_bounded kldstat >"$artifacts/guest-kldstat-after-load.txt" pre15_guest_ssh_bounded dmesg >"$artifacts/guest-dmesg-before.txt" diff -u "$artifacts/guest-dmesg-before-load.txt" \ "$artifacts/guest-dmesg-before.txt" >"$artifacts/guest-dmesg-load.diff" || true sed -n '/^+++ /d; /^+/s/^+//p' "$artifacts/guest-dmesg-load.diff" \ >"$artifacts/guest-dmesg-load-added.txt" if grep -Eqi 'panic:|lock order reversal|witness.*warning|use-after-free|fatal trap|pager fault|linker.*(error|undefined)|undefined symbol' \ "$artifacts/guest-dmesg-load-added.txt"; then pre15_dut_fail 'B33 exact-source KLD load produced kernel or linker errors' fi pre15_own_guest_kld B33-erofs-zstdio0.ko 'B33 exact-source KLD' pre15_guest_ssh_bounded kldstat >"$artifacts/guest-kldstat-before-trace.txt" if ! pre15_guest_ssh_bounded command -v dtrace \ >"$artifacts/guest-dtrace-command.txt" 2>"$artifacts/guest-dtrace-command.stderr"; then pre15_infra_blocked 'guest dtrace tool is unavailable for B33 call proof' fi set +e pre15_guest_ssh_bounded kldload dtraceall \ >"$artifacts/guest-dtrace-kldload.stdout" \ 2>"$artifacts/guest-dtrace-kldload.stderr" dtrace_load_rc=$? set -e pre15_guest_ssh_bounded kldstat >"$artifacts/guest-kldstat-after-trace.txt" awk 'NR == FNR { if (FNR > 1) seen[$5] = 1; next } FNR > 1 && !seen[$5] { print $1 "\t" $5 }' \ "$artifacts/guest-kldstat-before-trace.txt" \ "$artifacts/guest-kldstat-after-trace.txt" \ >"$artifacts/guest-trace-klds-all.tsv" awk '$2 == "dtraceall.ko"' "$artifacts/guest-trace-klds-all.tsv" \ >"$artifacts/guest-trace-klds.tsv" while IFS="$(printf '\t')" read -r trace_kld_id trace_kld_name; do test -n "$trace_kld_id" || continue pre15_own_guest_kld "$trace_kld_name" 'B33 FBT call-proof dependency' done <"$artifacts/guest-trace-klds.tsv" test "$dtrace_load_rc" -eq 0 || \ pre15_infra_blocked 'guest dtraceall KLD failed to load for B33 call proof' if ! pre15_guest_ssh_bounded \ "dtrace -l -n 'fbt::z_erofs_extent_cache_init:entry' -n 'fbt::z_erofs_do_read:entry'" \ >"$artifacts/guest-dtrace-probe-list.txt" \ 2>"$artifacts/guest-dtrace-probe-list.stderr"; then pre15_infra_blocked 'B33 cache init/read FBT probes are unavailable' fi grep -q 'z_erofs_extent_cache_init.*entry' \ "$artifacts/guest-dtrace-probe-list.txt" || \ pre15_runner_fail 'B33 cache init FBT probe was not listed exactly' grep -q 'z_erofs_do_read.*entry' \ "$artifacts/guest-dtrace-probe-list.txt" || \ pre15_runner_fail 'B33 compressed-read FBT probe was not listed exactly' b33_unown() { kind=$1 value=$2 tmp=$PRE15_CASE_TMP/ownership.$$ awk -F ' ' -v kind="$kind" -v value="$value" \ '!($1 == kind && $2 == value)' "$PRE15_OWNERSHIP_FILE" >"$tmp" mv "$tmp" "$PRE15_OWNERSHIP_FILE" } b33_attach() { image=$1 label=$2 B33_MD=$(pre15_guest_ssh_bounded mdconfig -a -t vnode \ -f "/root/B33-fixtures/images/$image") || \ pre15_dut_fail "$label md attach failed" case "$B33_MD" in md[0-9]*) ;; *) pre15_runner_fail "unexpected B33 md unit: $B33_MD" ;; esac pre15_own_guest_md "$B33_MD" "$label md" B33_MOUNT=/mnt/pre15-b33-$label pre15_guest_ssh_bounded mkdir -p "$B33_MOUNT" if test "${B33_TRACE_MOUNT:-0}" = 1; then mount_trace=/tmp/pre15-b33-cache-init.trace pre15_guest_ssh_bounded \ "dtrace -q -o '$mount_trace' -n 'fbt::z_erofs_extent_cache_init:entry { @calls = count(); } END { printa(@calls); }' -c 'mount -t erofs -o ro /dev/$B33_MD $B33_MOUNT'" \ >"$artifacts/valid-mount.stdout" \ 2>"$artifacts/valid-mount.stderr" || \ pre15_dut_fail "$label traced mount failed" pre15_guest_ssh_bounded cat "$mount_trace" \ >"$artifacts/guest-cache-init-fbt.txt" if ! awk '$1 ~ /^[0-9]+$/ && $1 > 0 { found = 1 } END { exit !found }' "$artifacts/guest-cache-init-fbt.txt"; then pre15_dut_fail 'B33 guest mount did not call decoded-cache init' fi else pre15_guest_ssh_bounded mount -t erofs -o ro "/dev/$B33_MD" "$B33_MOUNT" || \ pre15_dut_fail "$label mount failed" fi pre15_own_guest_mount "$B33_MOUNT" "$label mount" } b33_detach() { if pre15_guest_ssh_bounded mount | grep -F " on $B33_MOUNT " >/dev/null; then pre15_guest_ssh_bounded umount "$B33_MOUNT" || \ pre15_dut_fail "$B33_MOUNT unmount failed" fi b33_unown guest-mount "$B33_MOUNT" pre15_guest_ssh_bounded mdconfig -d -u "$B33_MD" || \ pre15_dut_fail "$B33_MD detach failed" b33_unown guest-md "$B33_MD" } reference=/root/B33-fixtures/source/payload.bin B33_TRACE_MOUNT=1 b33_attach lz4-valid.erofs valid unset B33_TRACE_MOUNT trace_file=/tmp/pre15-b33-do-read.trace if ! pre15_guest_ssh_bounded \ "dtrace -q -o '$trace_file' -n 'fbt::z_erofs_do_read:entry { @calls = count(); } END { printa(@calls); }' -c '/root/B33-cache-probe concurrent $B33_MOUNT/payload.bin $reference 0 64 4 0 65536'" \ >"$artifacts/valid-concurrent.stdout" \ 2>"$artifacts/valid-concurrent.stderr"; then pre15_dut_fail 'TC168 valid full-extent concurrent reads diverged' fi if ! grep -qx 'concurrent PASS workers=64 loops=4 assertions=256 errno=0 offset=0 length=65536' \ "$artifacts/valid-concurrent.stdout"; then pre15_runner_fail 'TC168 valid assertion count was not exactly 256' fi pre15_guest_ssh_bounded cat "$trace_file" \ >"$artifacts/guest-do-read-fbt.txt" if ! awk '$1 ~ /^[0-9]+$/ && $1 > 0 { found = 1 } END { exit !found }' "$artifacts/guest-do-read-fbt.txt"; then pre15_dut_fail 'B33 guest reads did not call the target read implementation' fi b33_detach b33_attach lz4-truncated.erofs truncated if ! pre15_guest_ssh_bounded /root/B33-cache-probe concurrent \ "$B33_MOUNT/payload.bin" "$reference" 97 1 1 0 65536 \ >"$artifacts/failure-single.stdout" \ 2>"$artifacts/failure-single.stderr"; then pre15_dut_fail 'TC168 single corrupt read did not return EINTEGRITY' fi if ! grep -qx 'concurrent PASS workers=1 loops=1 assertions=1 errno=97 offset=0 length=65536' \ "$artifacts/failure-single.stdout"; then pre15_runner_fail 'TC168 single corrupt assertion count was not 1' fi if ! pre15_guest_ssh_bounded /root/B33-cache-probe concurrent \ "$B33_MOUNT/payload.bin" "$reference" 97 64 2 0 65536 \ >"$artifacts/failure-concurrent.stdout" \ 2>"$artifacts/failure-concurrent.stderr"; then pre15_dut_fail 'TC168 failure waiters did not receive EINTEGRITY' fi if ! grep -qx 'concurrent PASS workers=64 loops=2 assertions=128 errno=97 offset=0 length=65536' \ "$artifacts/failure-concurrent.stdout"; then pre15_runner_fail 'TC168 failure assertion count was not exactly 128' fi b33_detach pre15_guest_ssh_bounded dmesg >"$artifacts/guest-dmesg-after.txt" diff -u "$artifacts/guest-dmesg-before.txt" \ "$artifacts/guest-dmesg-after.txt" >"$artifacts/guest-dmesg.diff" || true sed -n '/^+++ /d; /^+/s/^+//p' "$artifacts/guest-dmesg.diff" \ >"$artifacts/guest-dmesg-added.txt" if grep -Eqi 'panic:|lock order reversal|witness.*warning|use-after-free|fatal trap|pager fault|linker.*(error|undefined)|undefined symbol' \ "$artifacts/guest-dmesg-added.txt"; then pre15_dut_fail 'TC168 produced panic, WITNESS, or UAF evidence' fi printf '%s\n' \ 'TC168-cache-inflight B33 QEMU PASS' \ 'Exact KLD ownership, FBT-proven cache init/read calls, full-extent success/failure waiters, unmount drain, and cleanup exercised' \ 'TC130 and full feature suite NOT_RUN'