This commit is contained in:
2026-08-18 09:20:44 +02:00
commit b826cd721a
522 changed files with 93730 additions and 0 deletions
+412
View File
@@ -0,0 +1,412 @@
#!/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=083acc65f842c409c5a2e089e50060d0062b677c
fixture_dir=$PRE15_DUT/tests/pre15/fixtures
spec=$fixture_dir/B23-explicit-spec.json
generator=$fixture_dir/B23-explicit-generate.py
oracle=$fixture_dir/B23-explicit-oracle.py
probe=$fixture_dir/B23-explicit-probe.c
kld_builder=$fixture_dir/B23-build-kld.sh
zmap=$PRE15_DUT/src/zmap.c
linux_zmap=$PRE15_ROOT/src-linux/zmap.c
artifacts=$PRE15_RUN_DIR/artifacts
first=$PRE15_CASE_TMP/first
second=$PRE15_CASE_TMP/second
for tool in cc diff git mkfs.erofs python3 sha256sum; do
command -v "$tool" >/dev/null 2>&1 || \
pre15_infra_blocked "missing B23 host tool: $tool"
done
pre15_record_fixture b23-spec "$spec"
pre15_record_fixture b23-generator "$generator"
pre15_record_fixture b23-oracle "$oracle"
pre15_record_fixture b23-probe "$probe"
pre15_record_fixture b23-kld-builder "$kld_builder"
pre15_record_fixture b23-case "$PRE15_DUT/tests/pre15/cases/B23-explicit-table.sh"
pre15_record_fixture b23-zmap "$zmap"
pre15_record_fixture b23-linux-zmap "$linux_zmap"
mkdir -p "$artifacts"
if ! python3 -B "$generator" --spec "$spec" --output "$first" \
>"$artifacts/B23-generate-first.stdout" \
2>"$artifacts/B23-generate-first.stderr"; then
pre15_runner_fail 'B23 first fixture generation failed'
fi
if ! python3 -B "$generator" --spec "$spec" --output "$second" \
>"$artifacts/B23-generate-second.stdout" \
2>"$artifacts/B23-generate-second.stderr"; then
pre15_runner_fail 'B23 repeat fixture generation failed'
fi
if ! diff -u "$first/SHA256SUMS" "$second/SHA256SUMS" \
>"$artifacts/B23-repeat-sums.diff" || \
! diff -u "$first/fixture-manifest.json" "$second/fixture-manifest.json" \
>"$artifacts/B23-repeat-manifest.diff"; then
pre15_runner_fail 'B23 fixture generation is not byte deterministic'
fi
if ! python3 -B "$oracle" --spec "$spec" --fixtures "$first" \
--report "$artifacts/B23-oracle-first.json" \
>"$artifacts/B23-oracle-first.stdout" \
2>"$artifacts/B23-oracle-first.stderr"; then
pre15_runner_fail 'B23 independent fixture oracle failed'
fi
if ! python3 -B "$oracle" --spec "$spec" --fixtures "$second" \
--report "$artifacts/B23-oracle-second.json" \
>"$artifacts/B23-oracle-second.stdout" \
2>"$artifacts/B23-oracle-second.stderr"; then
pre15_runner_fail 'B23 repeat fixture oracle failed'
fi
cp "$first/SHA256SUMS" "$artifacts/B23-fixture-SHA256SUMS"
cp "$first/fixture-manifest.json" "$artifacts/B23-fixture-manifest.json"
pre15_record_fixture b23-generated-sums "$artifacts/B23-fixture-SHA256SUMS"
if ! cc -std=c11 -Wall -Wextra -Werror -c "$probe" \
-o "$PRE15_CASE_TMP/B23-explicit-probe.o" \
>"$artifacts/B23-probe-compile.stdout" \
2>"$artifacts/B23-probe-compile.stderr"; then
pre15_runner_fail 'B23 guest probe does not compile on the host'
fi
if python3 -B - "$PRE15_ROOT" "$PRE15_DUT" "$baseline" "$spec" \
"$artifacts/B23-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]
spec_path = Path(sys.argv[4])
report_path = Path(sys.argv[5])
def committed(path: str) -> str:
completed = subprocess.run(
["git", "-C", str(root), "show", f"{baseline}:repo-pre-15/{path}"],
check=False,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
if completed.returncode != 0:
raise SystemExit(f"cannot read B23 baseline {path}: {completed.stderr}")
return completed.stdout
def function(source: str, name: str) -> str:
match = re.search(r"^" + re.escape(name) + r"\s*\(", source, re.MULTILINE)
if match is None:
raise SystemExit(f"missing function: {name}")
start = source.rfind("\n", 0, source.rfind("\n", 0, match.start())) + 1
brace = source.find("{", match.end())
depth = 0
state = "code"
index = brace
while index < len(source):
char = source[index]
following = source[index + 1] if index + 1 < len(source) else ""
if state == "code":
if char == "/" and following == "*":
state = "block"
index += 2
continue
if char == "/" and following == "/":
state = "line"
index += 2
continue
if char == '"':
state = "string"
elif char == "'":
state = "character"
elif char == "{":
depth += 1
elif char == "}":
depth -= 1
if depth == 0:
return source[start : index + 1]
elif state == "block" and char == "*" and following == "/":
state = "code"
index += 2
continue
elif state == "line" and char == "\n":
state = "code"
elif state in {"string", "character"}:
if char == "\\":
index += 2
continue
if (state == "string" and char == '"') or (
state == "character" and char == "'"
):
state = "code"
index += 1
raise SystemExit(f"unterminated function: {name}")
spec = json.loads(spec_path.read_text(encoding="ascii"))
current = (dut / "src/zmap.c").read_text(encoding="utf-8")
before = committed("src/zmap.c")
validator = function(current, "z_erofs_validate_extent_table")
old_validator = function(before, "z_erofs_validate_extent_table")
if validator == old_validator:
raise SystemExit("B23 validator did not change")
if function(current, "z_erofs_read_extent") != function(before, "z_erofs_read_extent"):
raise SystemExit("B23 changed the local record decoder")
for name in ("z_erofs_map_blocks_ext", "z_erofs_fill_inode"):
if function(current, name) != function(before, name):
raise SystemExit(f"B23 changed {name}")
if "#define Z_EROFS_EXTENT_VALIDATE_CHUNK_SIZE (64 * 1024)" not in current:
raise SystemExit("B23 fixed validation chunk bound is missing")
required = (
"z_erofs_read_extent(sbi, vi, record_pos, recsz, &ext)",
"while (scan_pos < table_end)",
"Z_EROFS_EXTENT_VALIDATE_CHUNK_SIZE",
"erofs_read_metadata(sbi, vi->nid, scan_pos, chunk_len, &buf)",
"memcpy(&ext, (const char *)buf.data + offset, recsz)",
"z_erofs_extent_lstart(&ext, recsz)",
"erofs_put_metabuf(&buf)",
"return (index == vi->z_extents ? 0 : EINTEGRITY)",
)
if not all(marker in validator for marker in required):
raise SystemExit("B23 validator is missing a bounded-scan contract marker")
if validator.count("z_erofs_read_extent(") != 1:
raise SystemExit("B23 short tail probe count changed")
if validator.count("erofs_read_metadata(") != 1:
raise SystemExit("B23 long-table reader is not chunk-scoped")
if re.search(r"return\s*\(\s*-E[A-Z0-9_]+", validator):
raise SystemExit("B23 introduced Linux negative errno")
allowed = {
"repo-pre-15/src/zmap.c",
"repo-pre-15/tests/pre15/cases/B23-explicit-table.sh",
"repo-pre-15/tests/pre15/fixtures/B23-build-kld.sh",
"repo-pre-15/tests/pre15/fixtures/B23-explicit-generate.py",
"repo-pre-15/tests/pre15/fixtures/B23-explicit-oracle.py",
"repo-pre-15/tests/pre15/fixtures/B23-explicit-probe.c",
"repo-pre-15/tests/pre15/fixtures/B23-explicit-spec.json",
}
changed = set(
subprocess.run(
["git", "-C", str(root), "diff", "--name-only", baseline, "--", "repo-pre-15"],
check=True,
text=True,
stdout=subprocess.PIPE,
).stdout.splitlines()
)
changed.update(
subprocess.run(
[
"git", "-C", str(root), "ls-files", "--others",
"--exclude-standard", "--", "repo-pre-15",
],
check=True,
text=True,
stdout=subprocess.PIPE,
).stdout.splitlines()
)
if changed != allowed:
raise SystemExit(f"B23 write set differs: {sorted(changed ^ allowed)}")
for path in (
"src/erofs_fs.h",
"src/data.c",
"src/decompressor.c",
"src/decompressor_lz4.c",
"src/decompressor_lzma.c",
"src/decompressor_deflate.c",
"src/decompressor_zstd.c",
"src/internal.h",
):
if (dut / path).read_text(encoding="utf-8") != committed(path):
raise SystemExit(f"B23 changed an excluded provider/codec/ABI file: {path}")
linux = (root / "src-linux/zmap.c").read_text(encoding="utf-8")
for marker in (
"recsz <= offsetof(struct z_erofs_extent, pstart_hi)",
"le64_to_cpu(*(__le64 *)ext)",
"le32_to_cpu(ext->pstart_lo)",
"le32_to_cpu(ext->pstart_hi) << 32",
"le32_to_cpu(ext->lstart_hi) << 32",
"erofs_inode_in_metabox(inode)",
):
if marker not in linux:
raise SystemExit(f"Linux explicit-table anchor changed: {marker}")
report = {
"baseline": baseline,
"batch": "B23",
"candidates": spec["candidates"],
"chunk_size": spec["chunk_size"],
"decompressor_files_unchanged": True,
"freebsd_positive_errno": True,
"linux_record_decode_audited": True,
"ondisk_header_unchanged": True,
"primary_metabox_reader_unchanged": True,
"status": "PASS",
"test": spec["test"],
"write_set": sorted(changed),
}
report_path.write_text(
json.dumps(report, ensure_ascii=True, indent=2, sort_keys=True) + "\n",
encoding="ascii",
)
print("B23 source/write-set/Linux-FreeBSD audit PASS")
PY
then
:
else
pre15_dut_fail 'B23 source, write-set, or cross-tree audit failed'
fi
sha256sum "$artifacts/B23-fixture-SHA256SUMS" \
"$artifacts/B23-fixture-manifest.json" \
"$artifacts/B23-oracle-first.json" \
"$artifacts/B23-oracle-second.json" \
"$artifacts/B23-source-audit.json" \
>"$artifacts/SHA256SUMS"
pre15_target_reached
if test "${PRE15_MODE:-host}" != qemu; then
printf '%s\n' \
'TC171 host real-fixture, independent oracle, and bounded source audit PASS' \
'QEMU exact-source mapped/backing runtime NOT_RUN in host mode' \
'Full feature suite NOT_RUN'
exit 0
fi
for tool in awk clang file nm scp tar; do
command -v "$tool" >/dev/null 2>&1 || \
pre15_infra_blocked "missing B23 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}"
fixture_archive=$PRE15_CASE_TMP/B23-fixtures.tar.gz
module=$PRE15_CASE_TMP/B23-erofs.ko
if ! /bin/sh "$kld_builder" "$PRE15_DUT" "$PRE15_FREEBSD_SRC" "$module" \
"$PRE15_CASE_TMP/kld-work" >"$artifacts/B23-kld-build.stdout" \
2>"$artifacts/B23-kld-build.stderr"; then
pre15_dut_fail 'B23 cross-target zstdio0 KLD build failed'
fi
pre15_record_module "$module"
file "$module" >"$artifacts/B23-kld-file.txt"
sha256sum "$module" >"$artifacts/B23-kld-sha256.txt"
nm -u "$module" | LC_ALL=C sort >"$artifacts/B23-kld-nm-u.txt"
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"
}
if ! pre15_scp "$fixture_archive" /root/B23-fixtures.tar.gz || \
! pre15_scp "$probe" /root/B23-explicit-probe.c || \
! pre15_scp "$module" /root/B23-erofs.ko; then
pre15_infra_blocked 'could not transfer B23 module, probe, or fixtures'
fi
if ! pre15_guest_ssh_bounded \
'rm -rf /root/pre15-b23-fixtures && mkdir /root/pre15-b23-fixtures && tar -xzf /root/B23-fixtures.tar.gz -C /root/pre15-b23-fixtures'; then
pre15_infra_blocked 'could not prepare B23 guest fixtures'
fi
if pre15_guest_ssh_bounded kldstat -n erofs >/dev/null 2>&1; then
pre15_infra_blocked 'FreeBSD guest already has an EROFS module loaded'
fi
if ! pre15_guest_ssh_bounded kldload /root/B23-erofs.ko \
>"$artifacts/B23-kldload.stdout" 2>"$artifacts/B23-kldload.stderr"; then
if grep -q 'module already loaded or in kernel' "$artifacts/B23-kldload.stderr"; then
pre15_infra_blocked 'guest kernel already owns the erofs.1 interface'
fi
pre15_dut_fail 'B23 exact-source KLD failed to load'
fi
pre15_own_guest_kld erofs 'B23 exact-source KLD'
if ! pre15_guest_ssh_bounded cc -std=c11 -Wall -Wextra -Werror \
-o /root/B23-explicit-probe /root/B23-explicit-probe.c; then
pre15_infra_blocked 'could not compile the B23 guest probe'
fi
python3 -B - "$first/fixture-manifest.json" >"$PRE15_CASE_TMP/B23-qemu-cases.tsv" <<'PY'
import json
from pathlib import Path
import sys
manifest = json.loads(Path(sys.argv[1]).read_text(encoding="ascii"))
for name, case in sorted(manifest["cases"].items()):
offsets = ",".join(str(value) for value in case["probe_offsets"])
print(
name,
case["image"],
case["expected_errno"],
case["file_size"],
offsets,
case["target_marker"],
sep="\t",
)
PY
pre15_attach_md()
{
pre15_md=$(pre15_guest_ssh_bounded mdconfig -a -t vnode -f "$1") || \
pre15_dut_fail "could not attach B23 provider: $1"
case "$pre15_md" in
md[0-9]*) ;;
*) pre15_runner_fail "unexpected mdconfig output: $pre15_md" ;;
esac
pre15_md=${pre15_md#md}
pre15_own_guest_md "$pre15_md" "$2"
printf '%s\n' "$pre15_md"
}
while IFS="$(printf '\t')" read -r case_id image_name expected_errno \
file_size offsets target_marker; do
mountpoint=/mnt/pre15-b23-$case_id
unit=$(pre15_attach_md "/root/pre15-b23-fixtures/$image_name" \
"B23 $case_id primary")
pre15_guest_ssh_bounded mkdir -p "$mountpoint"
if ! pre15_guest_ssh_bounded mount -t erofs -o ro "/dev/md$unit" \
"$mountpoint"; then
pre15_dut_fail "B23 $case_id mount failed before $target_marker"
fi
pre15_own_guest_mount "$mountpoint" "B23 $case_id mount"
if test "$expected_errno" -eq 0; then
old_ifs=$IFS
IFS=,
set -- $offsets
IFS=$old_ifs
if ! pre15_guest_ssh_bounded /root/B23-explicit-probe pass \
"$mountpoint/target.bin" "$file_size" "$@"; then
pre15_dut_fail "B23 mapped positive failed at $target_marker"
fi
else
if ! pre15_guest_ssh_bounded /root/B23-explicit-probe errno \
"$mountpoint/target.bin" "$expected_errno"; then
pre15_dut_fail "B23 corruption result failed at $target_marker"
fi
fi
done <"$PRE15_CASE_TMP/B23-qemu-cases.tsv"
pre15_guest_ssh_bounded dmesg >"$artifacts/B23-dmesg.txt"
if grep -Eq 'panic:|Fatal trap|lock order reversal|KDB: stack backtrace' \
"$artifacts/B23-dmesg.txt"; then
pre15_dut_fail 'B23 runtime produced a kernel diagnostic'
fi
printf '%s\n' \
'TC171 QEMU explicit primary/metabox mapped and corruption runtime PASS' \
'All errno values are positive FreeBSD ABI values; full feature suite NOT_RUN'