update
This commit is contained in:
Executable
+604
@@ -0,0 +1,604 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
: "${PRE15_DUT:?PRE15_DUT is required}"
|
||||
: "${PRE15_ROOT:?PRE15_ROOT is required}"
|
||||
: "${PRE15_RUN_DIR:?PRE15_RUN_DIR is required}"
|
||||
: "${PRE15_LIB_DIR:?PRE15_LIB_DIR is required}"
|
||||
. "$PRE15_LIB_DIR/runner.sh"
|
||||
|
||||
baseline=08a567554881f19bcec933a92a46d18cfb73d22e
|
||||
artifacts=$PRE15_RUN_DIR/artifacts
|
||||
compact_fixture=$PRE15_DUT/tests/pre15/fixtures/B13-compact-time.json
|
||||
extended_fixture=$PRE15_DUT/tests/pre15/fixtures/B13-extended-time.json
|
||||
inode=$PRE15_DUT/src/inode.c
|
||||
internal=$PRE15_DUT/src/internal.h
|
||||
super=$PRE15_DUT/src/super.c
|
||||
ondisk=$PRE15_DUT/src/erofs_fs.h
|
||||
vnops=$PRE15_DUT/src/erofs_vnops.c
|
||||
|
||||
for tool in cc git python3 sha256sum; do
|
||||
command -v "$tool" >/dev/null 2>&1 || \
|
||||
pre15_infra_blocked "missing B13 host tool: $tool"
|
||||
done
|
||||
|
||||
pre15_record_fixture b13-compact "$compact_fixture"
|
||||
pre15_record_fixture b13-extended "$extended_fixture"
|
||||
pre15_record_fixture b13-inode "$inode"
|
||||
pre15_record_fixture b13-internal "$internal"
|
||||
pre15_record_fixture b13-super "$super"
|
||||
pre15_record_fixture b13-ondisk "$ondisk"
|
||||
pre15_record_fixture b13-vnops "$vnops"
|
||||
pre15_record_fixture b13-case "$PRE15_DUT/tests/pre15/cases/B13-time.sh"
|
||||
|
||||
mkdir -p "$artifacts"
|
||||
pre15_target_reached
|
||||
|
||||
if python3 - "$PRE15_ROOT" "$PRE15_DUT" "$baseline" "$compact_fixture" \
|
||||
"$extended_fixture" "$artifacts" <<'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]
|
||||
compact_path = Path(sys.argv[4])
|
||||
extended_path = Path(sys.argv[5])
|
||||
artifacts = Path(sys.argv[6])
|
||||
src = dut / "src"
|
||||
eintegrity = 97
|
||||
int64_min = -(1 << 63)
|
||||
int64_max = (1 << 63) - 1
|
||||
int32_min = -(1 << 31)
|
||||
int32_max = (1 << 31) - 1
|
||||
|
||||
|
||||
def committed(path: str) -> str:
|
||||
completed = subprocess.run(
|
||||
["git", "-C", str(root), "show", f"{baseline}:repo-pre-15/src/{path}"],
|
||||
check=False,
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
if completed.returncode != 0:
|
||||
raise SystemExit(f"cannot read B13 baseline {path}: {completed.stderr}")
|
||||
return completed.stdout
|
||||
|
||||
|
||||
def replace_once(source: str, old: str, new: str, label: str) -> str:
|
||||
count = source.count(old)
|
||||
if count != 1:
|
||||
raise SystemExit(f"{label}: expected one transform source, found {count}")
|
||||
return source.replace(old, new, 1)
|
||||
|
||||
|
||||
def extract_function(source: str, name: str) -> str:
|
||||
match = re.search(r"^" + re.escape(name) + r"\s*\(", source, re.MULTILINE)
|
||||
if not match:
|
||||
raise SystemExit(f"missing function: {name}")
|
||||
name_line = source.rfind("\n", 0, match.start()) + 1
|
||||
start = source.rfind("\n", 0, name_line - 1) + 1
|
||||
brace = source.find("{", match.end())
|
||||
if brace < 0:
|
||||
raise SystemExit(f"missing function body: {name}")
|
||||
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}")
|
||||
|
||||
|
||||
def load_fixture(path: Path, expected_encoding: set[str]) -> dict:
|
||||
fixture = json.loads(path.read_text(encoding="ascii"))
|
||||
if fixture.get("schema") != 1 or fixture.get("candidate") != "P15-018":
|
||||
raise SystemExit(f"invalid B13 fixture identity: {path.name}")
|
||||
if set(fixture.get("encoding", {})) != expected_encoding:
|
||||
raise SystemExit(f"invalid B13 fixture encoding keys: {path.name}")
|
||||
if not isinstance(fixture.get("cases"), list) or not fixture["cases"]:
|
||||
raise SystemExit(f"empty B13 fixture: {path.name}")
|
||||
return fixture
|
||||
|
||||
|
||||
def decode_le(raw: str, width: int, *, signed: bool) -> int:
|
||||
if not re.fullmatch(r"[0-9a-f]+", raw) or len(raw) != width * 2:
|
||||
raise SystemExit(f"invalid {width}-byte lowercase hex value: {raw!r}")
|
||||
return int.from_bytes(bytes.fromhex(raw), byteorder="little", signed=signed)
|
||||
|
||||
|
||||
def status_for_range(value: int, minimum: int, maximum: int) -> str:
|
||||
return "PASS" if minimum <= value <= maximum else "EINTEGRITY"
|
||||
|
||||
|
||||
current = {
|
||||
name: (src / name).read_text(encoding="utf-8")
|
||||
for name in ("internal.h", "inode.c", "super.c", "erofs_fs.h", "erofs_vnops.c")
|
||||
}
|
||||
base = {name: committed(name) for name in current}
|
||||
|
||||
expected_internal = replace_once(
|
||||
base["internal.h"], "\tuint64_t epoch;", "\tint64_t epoch;", "signed epoch field"
|
||||
)
|
||||
expected_internal = replace_once(
|
||||
expected_internal, "\tuint64_t mtime;", "\ttime_t mtime;", "time_t inode field"
|
||||
)
|
||||
if current["internal.h"] != expected_internal:
|
||||
raise SystemExit("internal.h differs from the two declared B13 type changes")
|
||||
|
||||
old_set_timestamp = """static int
|
||||
erofs_set_timestamp(struct erofs_inode *vi, uint64_t seconds,
|
||||
uint32_t nanoseconds)
|
||||
{
|
||||
\tif (nanoseconds >= 1000000000 || seconds > (uint64_t)INT64_MAX)
|
||||
\t\treturn (EINTEGRITY);
|
||||
\tvi->mtime = seconds;
|
||||
\tvi->mtime_nsec = nanoseconds;
|
||||
\treturn (0);
|
||||
}
|
||||
"""
|
||||
new_set_timestamp = """static int
|
||||
erofs_set_timestamp(struct erofs_inode *vi, int64_t seconds,
|
||||
uint32_t nanoseconds)
|
||||
{
|
||||
\ttime_t mtime;
|
||||
|
||||
\tif (nanoseconds >= 1000000000 ||
|
||||
\t __builtin_add_overflow(seconds, 0, &mtime))
|
||||
\t\treturn (EINTEGRITY);
|
||||
\tvi->mtime = mtime;
|
||||
\tvi->mtime_nsec = nanoseconds;
|
||||
\treturn (0);
|
||||
}
|
||||
"""
|
||||
expected_inode = replace_once(
|
||||
base["inode.c"], old_set_timestamp, new_set_timestamp, "timestamp conversion"
|
||||
)
|
||||
expected_inode = replace_once(
|
||||
expected_inode,
|
||||
"\tuint64_t addrmask, mtime;",
|
||||
"\tuint64_t addrmask;\n\tint64_t mtime;",
|
||||
"signed decoded mtime",
|
||||
)
|
||||
expected_inode = replace_once(
|
||||
expected_inode,
|
||||
"\t\t (uint64_t)le32toh(dic->i_mtime), &mtime)",
|
||||
"\t\t (int64_t)le32toh(dic->i_mtime), &mtime)",
|
||||
"compact signed checked add",
|
||||
)
|
||||
expected_inode = replace_once(
|
||||
expected_inode,
|
||||
"\t\terror = erofs_set_timestamp(vi, le64toh(die->i_mtime),\n"
|
||||
"\t\t le32toh(die->i_mtime_nsec));",
|
||||
"\t\terror = erofs_set_timestamp(vi,\n"
|
||||
"\t\t (int64_t)le64toh(die->i_mtime),\n"
|
||||
"\t\t le32toh(die->i_mtime_nsec));",
|
||||
"extended signed decode",
|
||||
)
|
||||
if current["inode.c"] != expected_inode:
|
||||
raise SystemExit("inode.c differs from the four declared B13 transforms")
|
||||
|
||||
expected_super = replace_once(
|
||||
base["super.c"],
|
||||
"\tsbi->epoch = le64toh(dsb->epoch);",
|
||||
"\tsbi->epoch = (int64_t)le64toh(dsb->epoch);",
|
||||
"super signed epoch decode",
|
||||
)
|
||||
if current["super.c"] != expected_super:
|
||||
raise SystemExit("super.c differs from the exact signed epoch transform")
|
||||
if current["erofs_fs.h"] != base["erofs_fs.h"]:
|
||||
raise SystemExit("B13 changed ondisk endian types, widths, or layout")
|
||||
if current["erofs_vnops.c"] != base["erofs_vnops.c"]:
|
||||
raise SystemExit("B13 changed FreeBSD VOP/timespec publication code")
|
||||
|
||||
ondisk_required = (
|
||||
"\t__le64 epoch;",
|
||||
"\t__le32 i_mtime;",
|
||||
"\t__le64 i_mtime;",
|
||||
"\t__le32 i_mtime_nsec;",
|
||||
)
|
||||
for declaration in ondisk_required:
|
||||
if current["erofs_fs.h"].count(declaration) != 1:
|
||||
raise SystemExit(f"ondisk timestamp declaration changed: {declaration}")
|
||||
|
||||
timestamp_helper = extract_function(current["inode.c"], "erofs_set_timestamp")
|
||||
read_inode = extract_function(current["inode.c"], "erofs_read_inode")
|
||||
if timestamp_helper.count("__builtin_add_overflow(seconds, 0, &mtime)") != 1:
|
||||
raise SystemExit("time_t checked conversion is absent or duplicated")
|
||||
compact_pattern = re.compile(
|
||||
r"__builtin_add_overflow\(sbi->epoch,\s*"
|
||||
r"\(int64_t\)le32toh\(dic->i_mtime\), &mtime\)"
|
||||
)
|
||||
if len(compact_pattern.findall(read_inode)) != 1:
|
||||
raise SystemExit("compact signed checked addition is absent or duplicated")
|
||||
if read_inode.count("(int64_t)le64toh(die->i_mtime)") != 1:
|
||||
raise SystemExit("extended signed seconds decode is absent or duplicated")
|
||||
if "(uint64_t)le32toh(dic->i_mtime)" in read_inode:
|
||||
raise SystemExit("unsigned compact timestamp arithmetic remains")
|
||||
|
||||
changed_sources = current["internal.h"] + current["inode.c"] + current["super.c"]
|
||||
if "ckd_add" in changed_sources or "stdckdint" in changed_sources:
|
||||
raise SystemExit("unsupported checked-arithmetic API entered B13")
|
||||
if re.search(r"return\s*\(\s*-E[A-Z0-9_]+", changed_sources):
|
||||
raise SystemExit("negative errno entered the FreeBSD timestamp path")
|
||||
|
||||
overflow_lines = []
|
||||
for path in ("inode.c", "super.c"):
|
||||
for line_number, line in enumerate(current[path].splitlines(), start=1):
|
||||
if "__builtin_" in line and "overflow" in line:
|
||||
overflow_lines.append(f"{path}:{line_number}:{line.strip()}")
|
||||
(artifacts / "B13-overflow-scan.txt").write_text(
|
||||
"\n".join(overflow_lines) + "\n", encoding="ascii"
|
||||
)
|
||||
|
||||
compact = load_fixture(
|
||||
compact_path, {"epoch", "fixed_nsec", "mtime_delta"}
|
||||
)
|
||||
extended = load_fixture(extended_path, {"mtime", "mtime_nsec"})
|
||||
seen_ids: set[str] = set()
|
||||
decoded_compact = []
|
||||
decoded_extended = []
|
||||
|
||||
for case in compact["cases"]:
|
||||
case_id = case.get("id")
|
||||
if not isinstance(case_id, str) or case_id in seen_ids:
|
||||
raise SystemExit(f"invalid or duplicate B13 case id: {case_id!r}")
|
||||
seen_ids.add(case_id)
|
||||
epoch = decode_le(case["epoch_le"], 8, signed=True)
|
||||
delta = decode_le(case["mtime_delta_le"], 4, signed=False)
|
||||
nanoseconds = decode_le(case["fixed_nsec_le"], 4, signed=False)
|
||||
mathematical = epoch + delta
|
||||
add_status = status_for_range(mathematical, int64_min, int64_max)
|
||||
if add_status != case["expected_add"]:
|
||||
raise SystemExit(f"compact add oracle mismatch: {case_id}")
|
||||
if add_status == "PASS":
|
||||
if mathematical != case["expected_seconds"]:
|
||||
raise SystemExit(f"compact seconds oracle mismatch: {case_id}")
|
||||
if status_for_range(mathematical, int64_min, int64_max) != case["expected_time64"]:
|
||||
raise SystemExit(f"compact time64 oracle mismatch: {case_id}")
|
||||
if status_for_range(mathematical, int32_min, int32_max) != case["expected_time32"]:
|
||||
raise SystemExit(f"compact time32 oracle mismatch: {case_id}")
|
||||
nsec_status = "PASS" if nanoseconds < 1_000_000_000 else "EINTEGRITY"
|
||||
if nsec_status != case["expected_nsec"]:
|
||||
raise SystemExit(f"compact nsec oracle mismatch: {case_id}")
|
||||
elif any(case[key] != "NOT_REACHED" for key in (
|
||||
"expected_time32", "expected_time64", "expected_nsec"
|
||||
)) or case["expected_seconds"] is not None:
|
||||
raise SystemExit(f"compact overflow must stop before conversion: {case_id}")
|
||||
decoded_compact.append(
|
||||
{
|
||||
"id": case_id,
|
||||
"epoch": epoch,
|
||||
"delta": delta,
|
||||
"nanoseconds": nanoseconds,
|
||||
"mathematical_seconds": mathematical,
|
||||
"add_status": add_status,
|
||||
}
|
||||
)
|
||||
|
||||
for case in extended["cases"]:
|
||||
case_id = case.get("id")
|
||||
if not isinstance(case_id, str) or case_id in seen_ids:
|
||||
raise SystemExit(f"invalid or duplicate B13 case id: {case_id!r}")
|
||||
seen_ids.add(case_id)
|
||||
seconds = decode_le(case["mtime_le"], 8, signed=True)
|
||||
nanoseconds = decode_le(case["mtime_nsec_le"], 4, signed=False)
|
||||
if seconds != case["expected_seconds"]:
|
||||
raise SystemExit(f"extended signed decode mismatch: {case_id}")
|
||||
if status_for_range(seconds, int64_min, int64_max) != case["expected_time64"]:
|
||||
raise SystemExit(f"extended time64 oracle mismatch: {case_id}")
|
||||
if status_for_range(seconds, int32_min, int32_max) != case["expected_time32"]:
|
||||
raise SystemExit(f"extended time32 oracle mismatch: {case_id}")
|
||||
nsec_status = "PASS" if nanoseconds < 1_000_000_000 else "EINTEGRITY"
|
||||
if nsec_status != case["expected_nsec"]:
|
||||
raise SystemExit(f"extended nsec oracle mismatch: {case_id}")
|
||||
decoded_extended.append(
|
||||
{"id": case_id, "seconds": seconds, "nanoseconds": nanoseconds}
|
||||
)
|
||||
|
||||
fixture_result = {
|
||||
"status": "PASS",
|
||||
"compact": decoded_compact,
|
||||
"extended": decoded_extended,
|
||||
}
|
||||
(artifacts / "B13-decoded-fixtures.json").write_text(
|
||||
json.dumps(fixture_result, ensure_ascii=True, indent=2, sort_keys=True) + "\n",
|
||||
encoding="ascii",
|
||||
)
|
||||
|
||||
|
||||
def c_i64(value: int) -> str:
|
||||
if value == int64_min:
|
||||
return "INT64_MIN"
|
||||
if value == int64_max:
|
||||
return "INT64_MAX"
|
||||
if value < 0:
|
||||
return f"(-INT64_C({-value}))"
|
||||
return f"INT64_C({value})"
|
||||
|
||||
|
||||
def c_status(value: str) -> str:
|
||||
if value == "PASS":
|
||||
return "0"
|
||||
if value == "EINTEGRITY":
|
||||
return "EINTEGRITY"
|
||||
raise SystemExit(f"cannot emit non-terminal status: {value}")
|
||||
|
||||
|
||||
program = f'''#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#define EINTEGRITY {eintegrity}
|
||||
|
||||
struct erofs_inode {{
|
||||
\ttime_t mtime;
|
||||
\tuint32_t mtime_nsec;
|
||||
}};
|
||||
|
||||
{timestamp_helper}
|
||||
|
||||
static int
|
||||
checked_compact(int64_t epoch, uint32_t delta, int64_t *seconds)
|
||||
{{
|
||||
\tif (__builtin_add_overflow(epoch, (int64_t)delta, seconds))
|
||||
\t\treturn (EINTEGRITY);
|
||||
\treturn (0);
|
||||
}}
|
||||
|
||||
static int
|
||||
checked_time32(int64_t seconds, int32_t *result)
|
||||
{{
|
||||
\treturn (__builtin_add_overflow(seconds, 0, result) ? EINTEGRITY : 0);
|
||||
}}
|
||||
|
||||
static int
|
||||
checked_time64(int64_t seconds, int64_t *result)
|
||||
{{
|
||||
\treturn (__builtin_add_overflow(seconds, 0, result) ? EINTEGRITY : 0);
|
||||
}}
|
||||
|
||||
static int
|
||||
check_timestamp(const char *id, int64_t seconds, uint32_t nanoseconds,
|
||||
int expected_time32, int expected_time64, int expected_nsec)
|
||||
{{
|
||||
\tstruct erofs_inode inode = {{ 0 }};
|
||||
\tint32_t time32;
|
||||
\tint64_t time64;
|
||||
\tint error, expected;
|
||||
|
||||
\tif (checked_time32(seconds, &time32) != expected_time32 ||
|
||||
\t checked_time64(seconds, &time64) != expected_time64) {{
|
||||
\t\tfprintf(stderr, "%s: representability\\n", id);
|
||||
\t\treturn (1);
|
||||
\t}}
|
||||
\texpected = expected_nsec != 0 ? expected_nsec :
|
||||
\t (sizeof(time_t) == sizeof(int32_t) ? expected_time32 : expected_time64);
|
||||
\terror = erofs_set_timestamp(&inode, seconds, nanoseconds);
|
||||
\tif (error != expected) {{
|
||||
\t\tfprintf(stderr, "%s: timestamp errno %d != %d\\n", id, error, expected);
|
||||
\t\treturn (1);
|
||||
\t}}
|
||||
\tif (error == 0 && ((int64_t)inode.mtime != seconds ||
|
||||
\t inode.mtime_nsec != nanoseconds)) {{
|
||||
\t\tfprintf(stderr, "%s: timestamp value\\n", id);
|
||||
\t\treturn (1);
|
||||
\t}}
|
||||
\treturn (0);
|
||||
}}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{{
|
||||
\tint64_t seconds;
|
||||
\tint error;
|
||||
|
||||
\tif (sizeof(time_t) != sizeof(int32_t) && sizeof(time_t) != sizeof(int64_t))
|
||||
\t\treturn (2);
|
||||
'''
|
||||
for case, decoded in zip(compact["cases"], decoded_compact, strict=True):
|
||||
epoch = decoded["epoch"]
|
||||
delta = decoded["delta"]
|
||||
expected_add = c_status(case["expected_add"])
|
||||
program += f'''\terror = checked_compact({c_i64(epoch)}, UINT32_C({delta}), &seconds);
|
||||
\tif (error != {expected_add}) {{
|
||||
\t\tfprintf(stderr, "{case['id']}: compact add errno\\n");
|
||||
\t\treturn (1);
|
||||
\t}}
|
||||
'''
|
||||
if case["expected_add"] == "PASS":
|
||||
program += f'''\tif (seconds != {c_i64(case['expected_seconds'])} ||
|
||||
\t check_timestamp("{case['id']}", seconds, UINT32_C({decoded['nanoseconds']}),
|
||||
\t {c_status(case['expected_time32'])}, {c_status(case['expected_time64'])},
|
||||
\t {c_status(case['expected_nsec'])}) != 0)
|
||||
\t\treturn (1);
|
||||
'''
|
||||
for case, decoded in zip(extended["cases"], decoded_extended, strict=True):
|
||||
program += f'''\tif (check_timestamp("{case['id']}", {c_i64(decoded['seconds'])},
|
||||
\t UINT32_C({decoded['nanoseconds']}), {c_status(case['expected_time32'])},
|
||||
\t {c_status(case['expected_time64'])}, {c_status(case['expected_nsec'])}) != 0)
|
||||
\t\treturn (1);
|
||||
'''
|
||||
program += f'''\tprintf("B13 boundary PASS compact={len(compact['cases'])} extended={len(extended['cases'])} time_t=%zu\\n",
|
||||
\t sizeof(time_t) * 8);
|
||||
\treturn (0);
|
||||
}}
|
||||
'''
|
||||
|
||||
program_path = artifacts / "B13-time-boundary.c"
|
||||
binary_path = artifacts / "B13-time-boundary"
|
||||
program_path.write_text(program, encoding="ascii")
|
||||
compiled = subprocess.run(
|
||||
[
|
||||
"cc", "-std=c11", "-O2", "-Wall", "-Wextra", "-Werror",
|
||||
str(program_path), "-o", str(binary_path),
|
||||
],
|
||||
check=False,
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
(artifacts / "B13-time-boundary.compile.stdout").write_text(
|
||||
compiled.stdout, encoding="utf-8"
|
||||
)
|
||||
(artifacts / "B13-time-boundary.compile.stderr").write_text(
|
||||
compiled.stderr, encoding="utf-8"
|
||||
)
|
||||
if compiled.returncode != 0:
|
||||
raise SystemExit("B13 boundary extractor compilation failed")
|
||||
executed = subprocess.run(
|
||||
[str(binary_path)],
|
||||
check=False,
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
(artifacts / "B13-time-boundary.stdout").write_text(
|
||||
executed.stdout, encoding="utf-8"
|
||||
)
|
||||
(artifacts / "B13-time-boundary.stderr").write_text(
|
||||
executed.stderr, encoding="utf-8"
|
||||
)
|
||||
if executed.returncode != 0:
|
||||
raise SystemExit("B13 boundary extractor execution failed")
|
||||
|
||||
program32 = program.replace("#include <time.h>\n", "#define time_t int32_t\n", 1)
|
||||
program32_path = artifacts / "B13-time-boundary32.c"
|
||||
binary32_path = artifacts / "B13-time-boundary32"
|
||||
program32_path.write_text(program32, encoding="ascii")
|
||||
compiled32 = subprocess.run(
|
||||
[
|
||||
"cc", "-std=c11", "-O2", "-Wall", "-Wextra", "-Werror",
|
||||
str(program32_path), "-o", str(binary32_path),
|
||||
],
|
||||
check=False,
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
(artifacts / "B13-time-boundary32.compile.stdout").write_text(
|
||||
compiled32.stdout, encoding="utf-8"
|
||||
)
|
||||
(artifacts / "B13-time-boundary32.compile.stderr").write_text(
|
||||
compiled32.stderr, encoding="utf-8"
|
||||
)
|
||||
if compiled32.returncode != 0:
|
||||
raise SystemExit("B13 32-bit time_t extractor compilation failed")
|
||||
executed32 = subprocess.run(
|
||||
[str(binary32_path)],
|
||||
check=False,
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
(artifacts / "B13-time-boundary32.stdout").write_text(
|
||||
executed32.stdout, encoding="utf-8"
|
||||
)
|
||||
(artifacts / "B13-time-boundary32.stderr").write_text(
|
||||
executed32.stderr, encoding="utf-8"
|
||||
)
|
||||
if executed32.returncode != 0:
|
||||
raise SystemExit("B13 32-bit time_t extractor execution failed")
|
||||
|
||||
result = {
|
||||
"status": "PASS",
|
||||
"final_id": "P15-018",
|
||||
"baseline": baseline,
|
||||
"compact_cases": len(compact["cases"]),
|
||||
"extended_cases": len(extended["cases"]),
|
||||
"signed_add_overflows": sum(
|
||||
case["expected_add"] == "EINTEGRITY" for case in compact["cases"]
|
||||
),
|
||||
"negative_seconds_passes": sum(
|
||||
case["expected_seconds"] is not None and case["expected_seconds"] < 0
|
||||
for case in compact["cases"] + extended["cases"]
|
||||
),
|
||||
"time32_lower_rejections": sum(
|
||||
case["expected_seconds"] is not None
|
||||
and case["expected_seconds"] < int32_min
|
||||
and case["expected_time32"] == "EINTEGRITY"
|
||||
for case in compact["cases"] + extended["cases"]
|
||||
),
|
||||
"time32_upper_rejections": sum(
|
||||
case["expected_seconds"] is not None
|
||||
and case["expected_seconds"] > int32_max
|
||||
and case["expected_time32"] == "EINTEGRITY"
|
||||
for case in compact["cases"] + extended["cases"]
|
||||
),
|
||||
"nanosecond_rejections": sum(
|
||||
case["expected_nsec"] == "EINTEGRITY"
|
||||
for case in compact["cases"] + extended["cases"]
|
||||
),
|
||||
"ondisk_header_byte_identical": True,
|
||||
"ondisk_seconds_width": 64,
|
||||
"ondisk_compact_delta_width": 32,
|
||||
"time_t_checked_conversion": True,
|
||||
"extracted_helper_time_widths": [32, 64],
|
||||
"unsupported_ckd_api": False,
|
||||
"positive_errno_preserved": True,
|
||||
"qemu": "NOT_RUN",
|
||||
"full_feature_suite": "NOT_RUN",
|
||||
}
|
||||
(artifacts / "B13-result.json").write_text(
|
||||
json.dumps(result, ensure_ascii=True, indent=2, sort_keys=True) + "\n",
|
||||
encoding="ascii",
|
||||
)
|
||||
print(
|
||||
f"B13 PASS compact={len(compact['cases'])} extended={len(extended['cases'])} "
|
||||
f"signed-overflow={result['signed_add_overflows']}"
|
||||
)
|
||||
PY
|
||||
then
|
||||
:
|
||||
else
|
||||
pre15_dut_fail 'B13 signed timestamp source or boundary check failed'
|
||||
fi
|
||||
|
||||
sha256sum "$artifacts/B13-overflow-scan.txt" \
|
||||
"$artifacts/B13-decoded-fixtures.json" \
|
||||
"$artifacts/B13-time-boundary.c" "$artifacts/B13-time-boundary" \
|
||||
"$artifacts/B13-time-boundary32.c" "$artifacts/B13-time-boundary32" \
|
||||
"$artifacts/B13-result.json" > "$artifacts/SHA256SUMS"
|
||||
printf 'B13 PASS signed timestamps and checked time_t boundaries\n'
|
||||
Reference in New Issue
Block a user