#!/bin/sh set -eu gate_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P) root=$(CDPATH= cd -- "$gate_dir/../../../.." && pwd -P) input=$gate_dir/P15-005-input.json mode=base base= output= oracle= while test "$#" -gt 0; do case "$1" in --base) test "$#" -ge 2 || { echo '--base requires a commit' >&2; exit 2; } mode=base base=$2 shift 2 ;; --worktree) mode=worktree shift ;; --output) test "$#" -ge 2 || { echo '--output requires a directory' >&2; exit 2; } output=$2 shift 2 ;; --oracle) test "$#" -ge 2 || { echo '--oracle requires a JSON file' >&2; exit 2; } oracle=$2 shift 2 ;; *) echo "unknown argument: $1" >&2 exit 2 ;; esac done test -n "$output" || { echo '--output is required' >&2; exit 2; } case "$output" in /*) ;; *) output=$PWD/$output ;; esac test ! -e "$output" || { echo "refusing existing output: $output" >&2; exit 2; } mkdir -p "$output" python3 - "$root" "$input" "$mode" "$base" "$output" "$oracle" <<'PY' from __future__ import annotations import hashlib import json from pathlib import Path import re import struct import subprocess import sys root = Path(sys.argv[1]) input_path = Path(sys.argv[2]) mode = sys.argv[3] requested_base = sys.argv[4] output = Path(sys.argv[5]) oracle_path = Path(sys.argv[6]) if sys.argv[6] else None spec = json.loads(input_path.read_text(encoding="ascii")) def run(argv: list[str], **kwargs: object) -> subprocess.CompletedProcess[str]: return subprocess.run(argv, check=True, text=True, **kwargs) def git(*args: str) -> str: return subprocess.check_output( ["git", "-C", str(root), *args], text=True ).strip() if mode == "base": if not requested_base: raise SystemExit("base mode requires --base") resolved = git("rev-parse", f"{requested_base}^{{commit}}") if resolved != spec["required_base"]: raise SystemExit( f"P15-005 must gate the frozen BASE {spec['required_base']}, got {resolved}" ) else: resolved = git("rev-parse", "HEAD") def source(path: str) -> str: if mode == "base": return subprocess.check_output( ["git", "-C", str(root), "show", f"{resolved}:{path}"], text=True, ) return (root / path).read_text(encoding="utf-8") sources = {path: source(path) for path in spec["source_paths"]} source_hashes = { path: hashlib.sha256(text.encode("utf-8")).hexdigest() for path, text in sources.items() } for path, expected in spec["linux_sha256"].items(): if source_hashes[path] != expected: raise SystemExit(f"Linux oracle identity mismatch for {path}") for path, anchors in spec["linux_semantic_anchors"].items(): for anchor in anchors: if anchor not in sources[path]: raise SystemExit(f"Linux semantic anchor absent in {path}: {anchor}") def extract_function(text: str, name: str) -> str: match = re.search(r"^" + re.escape(name) + r"\s*\(", text, re.MULTILINE) if not match: raise ValueError(f"function not found: {name}") start = text.rfind("\n\n", 0, match.start()) + 2 brace = text.find("{", match.end()) if brace < 0: raise ValueError(f"function has no body: {name}") depth = 0 state = "code" index = brace while index < len(text): char = text[index] following = text[index + 1] if index + 1 < len(text) 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 text[start : index + 1] + "\n" 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 ValueError(f"unterminated function: {name}") function_bodies: dict[str, str] = {} for key in spec["baseline_function_sha256"]: filename, function = key.split(":", 1) function_bodies[key] = extract_function( sources[f"repo-pre-15/src/{filename}"], function ) if mode == "base": for key, expected in spec["baseline_function_sha256"].items(): actual = hashlib.sha256(function_bodies[key].encode("utf-8")).hexdigest() if actual != expected: raise SystemExit(f"unreviewed baseline consumer body: {key}") def all_functions(text: str) -> dict[str, str]: result: dict[str, str] = {} for match in re.finditer(r"^([A-Za-z_][A-Za-z0-9_]*)\s*\(", text, re.MULTILINE): name = match.group(1) try: result[name] = extract_function(text, name) except ValueError: continue return result direct_sites: set[str] = set() for filename in ( "data.c", "decompressor.c", "inode.c", "super.c", "xattr.c", "zdata.c", "zmap.c", ): funcs = all_functions(sources[f"repo-pre-15/src/{filename}"]) for name, body in funcs.items(): calls = body[body.find("{") :] direct = "erofs_read_metadata(" in calls if filename in {"decompressor.c", "super.c", "xattr.c"}: direct = direct or "erofs_bread(" in calls if direct: direct_sites.add(f"{filename}:{name}") expected_direct = set(spec["direct_metadata_functions"]) if direct_sites != expected_direct: raise SystemExit( "direct metadata consumer inventory mismatch: " f"missing={sorted(expected_direct - direct_sites)} " f"extra={sorted(direct_sites - expected_direct)}" ) object_mode = "struct erofs_buf {" in sources["repo-pre-15/src/internal.h"] ownership_failures: list[dict[str, object]] = [] candidate_contract_result: dict[str, object] = {"status": "NOT_APPLICABLE"} if mode == "worktree": internal = sources["repo-pre-15/src/internal.h"] data_source = sources["repo-pre-15/src/data.c"] required_object_markers = [ "struct erofs_buf {", "void *data;", "void (*release)(void *);", "void erofs_put_metabuf(struct erofs_buf *buf);", "erofs_read_metadata(struct erofs_sb_info *sbi, erofs_nid_t nid,", "struct erofs_buf *buf)", ] for marker in required_object_markers: if marker not in internal and marker not in data_source: raise SystemExit(f"candidate metadata object marker absent: {marker}") if re.search( r"erofs_read_metadata\s*\([^;]*size_t\s+len\s*,\s*void\s*\*\*bufp\s*\)\s*;", internal, re.DOTALL, ): raise SystemExit("legacy metadata void-pointer prototype remains") contract = spec["candidate_ownership_contract"] contract_keys = set(contract["object_local_counts"]) if contract_keys != set(spec["baseline_function_sha256"]): raise SystemExit("candidate ownership contract does not cover every audited function") function_metrics = [] for key in sorted(contract_keys): body = function_bodies[key] calls = body[body.find("{") :] initialized_objects = re.findall( r"\bstruct\s+erofs_buf\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*" r"EROFS_BUF_INITIALIZER\s*;", calls, ) all_objects = re.findall( r"\bstruct\s+erofs_buf\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?:=|;)", calls, ) metrics = { "function": key, "object_local_count": len(initialized_objects), "put_count": calls.count("erofs_put_metabuf("), "legacy_release_count": calls.count("erofs_brelse("), "raw_bread_count": calls.count("erofs_bread("), } function_metrics.append(metrics) expected = { "object_local_count": contract["object_local_counts"][key], "put_count": contract["put_counts"][key], "legacy_release_count": contract["legacy_release_counts"].get(key, 0), "raw_bread_count": contract["raw_bread_counts"].get(key, 0), } for field, value in expected.items(): if metrics[field] != value: ownership_failures.append( { "function": key, "field": field, "expected": value, "actual": metrics[field], } ) if sorted(all_objects) != sorted(initialized_objects): ownership_failures.append( {"function": key, "field": "uninitialized-metadata-object"} ) for variable in initialized_objects: if f"&{variable}" not in calls: ownership_failures.append( {"function": key, "field": "object-address-unused", "variable": variable} ) if f"erofs_put_metabuf(&{variable})" not in calls: ownership_failures.append( {"function": key, "field": "object-not-released", "variable": variable} ) raw_pointer_names = re.findall( r"\bvoid\s*\*\s*([A-Za-z_][A-Za-z0-9_]*)\s*(?:[;=,])", calls ) for api in contract["acquire_apis"]: for call in re.findall( r"\b" + re.escape(api) + r"\s*\((.*?);", calls, re.DOTALL ): for variable in raw_pointer_names: if re.search(r"&\s*" + re.escape(variable) + r"\b", call): ownership_failures.append( { "function": key, "field": "raw-pointer-metadata-acquire", "api": api, "variable": variable, } ) candidate_contract_result = { "status": "PASS" if not ownership_failures else "FAIL", "function_metrics": function_metrics, "transfer_functions": contract["transfer_functions"], "failures": ownership_failures, } def compile_and_run(name: str, program: str) -> list[dict[str, int | str]]: source_path = output / f"{name}.c" binary_path = output / name compile_stdout = output / f"{name}.compile.stdout" compile_stderr = output / f"{name}.compile.stderr" run_stdout = output / f"{name}.stdout" run_stderr = output / f"{name}.stderr" source_path.write_text(program, encoding="ascii") compiler = subprocess.run( [ "cc", "-std=c17", "-O0", "-Wall", "-Wextra", "-Werror", str(source_path), "-o", str(binary_path), ], text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) compile_stdout.write_text(compiler.stdout, encoding="utf-8") compile_stderr.write_text(compiler.stderr, encoding="utf-8") if compiler.returncode != 0: raise SystemExit(f"{name} extractor compilation failed") executed = subprocess.run( [str(binary_path)], text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=60, ) run_stdout.write_text(executed.stdout, encoding="utf-8") run_stderr.write_text(executed.stderr, encoding="utf-8") if executed.returncode != 0: raise SystemExit(f"{name} extractor execution failed") records = [] for line in executed.stdout.splitlines(): fields = line.split("\t") if len(fields) != 11 or fields[0] != "tuple": raise SystemExit(f"invalid {name} extractor output: {line!r}") records.append( { "id": fields[1], "m_la": int(fields[2]), "m_pa": int(fields[3]), "m_llen": int(fields[4]), "m_plen": int(fields[5]), "m_deviceid": int(fields[6]), "m_flags": int(fields[7]), "errno": int(fields[8]), "acquire_count": int(fields[9]), "release_count": int(fields[10]), } ) return records def compile_helper(name: str, program: str) -> list[str]: source_path = output / f"{name}.c" binary_path = output / name compile_stdout = output / f"{name}.compile.stdout" compile_stderr = output / f"{name}.compile.stderr" run_stdout = output / f"{name}.stdout" run_stderr = output / f"{name}.stderr" source_path.write_text(program, encoding="ascii") compiler = subprocess.run( [ "cc", "-std=c17", "-O0", "-Wall", "-Wextra", "-Werror", str(source_path), "-o", str(binary_path), ], text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, ) compile_stdout.write_text(compiler.stdout, encoding="utf-8") compile_stderr.write_text(compiler.stderr, encoding="utf-8") if compiler.returncode != 0: raise SystemExit(f"{name} helper extractor compilation failed") executed = subprocess.run( [str(binary_path)], text=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=60, ) run_stdout.write_text(executed.stdout, encoding="utf-8") run_stderr.write_text(executed.stderr, encoding="utf-8") if executed.returncode != 0: raise SystemExit(f"{name} helper extractor execution failed") return executed.stdout.splitlines() common = r''' #include #include #include #include #include #include #define EIO 5 #define EINVAL 22 #define EOPNOTSUPP 45 #define EOVERFLOW 84 #define EINTEGRITY 97 #define MIN(a, b) ((a) < (b) ? (a) : (b)) #define roundup2(x, y) ((((x) + ((y) - 1)) / (y)) * (y)) #define rounddown2(x, y) ((x) - ((x) % (y))) #define bzero(p, n) memset((p), 0, (n)) #define le16toh(x) (x) #define le32toh(x) (x) #define le64toh(x) (x) typedef uint64_t erofs_nid_t; typedef uint64_t erofs_off_t; typedef uint64_t erofs_blk_t; static __attribute__((unused)) uint16_t le16dec(const void *pointer) { uint16_t value; memcpy(&value, pointer, sizeof(value)); return value; } static __attribute__((unused)) uint32_t le32dec(const void *pointer) { uint32_t value; memcpy(&value, pointer, sizeof(value)); return value; } static __attribute__((unused)) uint64_t le64dec(const void *pointer) { uint64_t value; memcpy(&value, pointer, sizeof(value)); return value; } ''' buffer_compat = r''' struct erofs_buf { void *data; void (*release)(void *); }; #define EROFS_BUF_INITIALIZER { .data = NULL, .release = NULL } static unsigned int gate_acquires; static unsigned int gate_releases; static const unsigned char *gate_bytes; static size_t gate_bytes_len; static uint64_t gate_bytes_base; static int gate_reader_mode; static void gate_release(void *data) { if (data != NULL) { ++gate_releases; free(data); } } static __attribute__((unused)) void erofs_brelse(void *data) { gate_release(data); } static __attribute__((unused)) void erofs_put_metabuf(struct erofs_buf *buf) { void *data; void (*release)(void *); if (buf == NULL || buf->data == NULL) return; data = buf->data; release = buf->release; buf->data = NULL; buf->release = NULL; if (release != NULL) release(data); } ''' def reader_definition(objects: bool) -> str: if objects: return r''' static int erofs_read_metadata(struct erofs_sb_info *sbi, erofs_nid_t nid, erofs_off_t off, size_t len, struct erofs_buf *buf) { void *data; (void)sbi; (void)nid; if (gate_reader_mode == 2 || off < gate_bytes_base || len > gate_bytes_len || off - gate_bytes_base > gate_bytes_len - len) return (EIO); data = malloc(len); if (data == NULL) return (EIO); memcpy(data, gate_bytes + (off - gate_bytes_base), len); buf->data = data; buf->release = gate_release; ++gate_acquires; return (0); } ''' return r''' static int erofs_read_metadata(struct erofs_sb_info *sbi, erofs_nid_t nid, erofs_off_t off, size_t len, void **bufp) { void *data; (void)sbi; (void)nid; if (gate_reader_mode == 2 || off < gate_bytes_base || len > gate_bytes_len || off - gate_bytes_base > gate_bytes_len - len) return (EIO); data = malloc(len); if (data == NULL) return (EIO); memcpy(data, gate_bytes + (off - gate_bytes_base), len); *bufp = data; ++gate_acquires; return (0); } ''' def c_bytes(data: bytes) -> str: if not data: return "0" return ", ".join(f"0x{byte:02x}" for byte in data) data_cases = [case for case in spec["tuple_cases"] if case["engine"] == "data"] data_case_code = [] for index, case in enumerate(data_cases): reader = case["reader"] raw = bytes.fromhex(reader.get("bytes_hex", "")) sbi = case["sbi"] inode = case["inode"] data_case_code.append( f''' static const unsigned char bytes_{index}[] = {{ {c_bytes(raw)} }}; struct erofs_sb_info sbi_{index} = {{ .block_size = {sbi['block_size']}ULL, .blkszbits = {sbi['blkszbits']}, .blocks = {sbi['blocks']}ULL, .device_id_mask = {sbi['device_id_mask']}, }}; struct erofs_inode vi_{index} = {{ .nid = {inode['nid']}ULL, .size = {inode['size']}ULL, .inode_off = {inode['inode_off']}ULL, .startblk = {inode['startblk']}ULL, .datalayout = {inode['datalayout']}, .inode_isize = {inode['inode_isize']}, .xattr_isize = {inode['xattr_isize']}, .chunkformat = {inode['chunkformat']}, .chunkbits = {inode['chunkbits']}, }}; gate_bytes = bytes_{index}; gate_bytes_len = {len(raw)}; gate_bytes_base = {inode['inode_off'] + inode['inode_isize'] + inode['xattr_isize']}ULL; gate_reader_mode = {2 if reader['mode'] == 'error' else 0}; gate_acquires = gate_releases = 0; pa = 0; device = 0; run = 0; hole = false; metadata = false; error = erofs_map_blocks(&sbi_{index}, &vi_{index}, {case['request']}ULL, &pa, &device, &run, &hole, &metadata); flags = (error == 0 && run != 0 && !hole ? EROFS_MAP_MAPPED : 0) | (metadata ? EROFS_MAP_META : 0); printf("tuple\\t{case['id']}\\t%llu\\t%llu\\t%zu\\t%zu\\t%u\\t%u\\t%d\\t%u\\t%u\\n", (unsigned long long){case['request']}ULL, (unsigned long long)pa, run, run, device, flags, error, gate_acquires, gate_releases); ''' ) data_preamble = common + buffer_compat + r''' #define EROFS_INODE_FLAT_PLAIN 0 #define EROFS_INODE_COMPRESSED_FULL 1 #define EROFS_INODE_FLAT_INLINE 2 #define EROFS_INODE_COMPRESSED_COMPACT 3 #define EROFS_INODE_CHUNK_BASED 4 #define EROFS_CHUNK_FORMAT_INDEXES 0x20 #define EROFS_CHUNK_FORMAT_48BIT 0x40 #define EROFS_BLOCK_MAP_ENTRY_SIZE 4 #define EROFS_DIRENT_NID_METABOX (1ULL << 63) #define EROFS_NULL_ADDR UINT64_MAX #define EROFS_MAP_MAPPED 0x0001 #define EROFS_MAP_META 0x0002 struct erofs_inode_chunk_index { uint16_t startblk_hi; uint16_t device_id; uint32_t startblk_lo; } __attribute__((packed)); struct erofs_inode { erofs_nid_t nid; uint64_t size; erofs_off_t inode_off; erofs_blk_t startblk; uint8_t datalayout; uint8_t inode_isize; uint32_t xattr_isize; uint16_t chunkformat; uint8_t chunkbits; }; struct erofs_sb_info { uint64_t block_size; uint8_t blkszbits; erofs_blk_t blocks; uint16_t device_id_mask; struct erofs_inode *metabox_en; }; static bool erofs_nid_in_metabox(erofs_nid_t nid) { return (nid & EROFS_DIRENT_NID_METABOX) != 0; } ''' data_program = ( data_preamble + reader_definition(object_mode) + extract_function(sources["repo-pre-15/src/data.c"], "erofs_inline_tail_start") + extract_function(sources["repo-pre-15/src/data.c"], "erofs_map_blocks_chunk") + extract_function(sources["repo-pre-15/src/data.c"], "erofs_map_blocks") + r''' int main(void) { erofs_off_t pa; unsigned int device, flags; size_t run; bool hole, metadata; int error; ''' + "".join(data_case_code) + "\treturn (0);\n}\n" ) zmap_cases = [case for case in spec["tuple_cases"] if case["engine"] == "zmap"] zmap_case_code = [] for index, case in enumerate(zmap_cases): reader = case["reader"] if "records" in reader: raw = b"".join( struct.pack( "> 32, record["lstart"] & 0xFFFFFFFF, record["lstart"] >> 32, )[:16] for record in reader["records"] ) else: raw = bytes.fromhex(reader.get("bytes_hex", "")) sbi = case["sbi"] inode = case["inode"] zmap_case_code.append( f''' static const unsigned char bytes_{index}[] = {{ {c_bytes(raw)} }}; struct erofs_inode packed_{index} = {{ .nid = 999, .size = {sbi['packed_size']}ULL }}; struct erofs_sb_info sbi_{index} = {{ .block_size = {sbi['block_size']}, .blkszbits = {sbi['blkszbits']}, .available_compr_algs = {sbi['available_compr_algs']}, .packed_inode = &packed_{index}, }}; struct erofs_inode vi_{index} = {{ .nid = {inode['nid']}ULL, .size = {inode['size']}ULL, .inode_off = {inode['inode_off']}ULL, .inode_isize = {inode['inode_isize']}, .xattr_isize = {inode['xattr_isize']}, .datalayout = {inode['datalayout']}, .z_advise = {inode['z_advise']}, .z_lclusterbits = {inode['z_lclusterbits']}, .z_extents = {inode['z_extents']}ULL, .z_initialized = true, }}; struct erofs_map_blocks map_{index} = {{ .m_la = {case['request']}ULL }}; gate_bytes = bytes_{index}; gate_bytes_len = {len(raw)}; gate_bytes_base = {reader.get('base', 0)}ULL; gate_reader_mode = {2 if reader['mode'] == 'error' else 0}; gate_acquires = gate_releases = 0; error = z_erofs_map_blocks_iter(&sbi_{index}, &vi_{index}, &map_{index}, 0); printf("tuple\\t{case['id']}\\t%llu\\t%llu\\t%llu\\t%llu\\t%u\\t%u\\t%d\\t%u\\t%u\\n", (unsigned long long)map_{index}.m_la, (unsigned long long)map_{index}.m_pa, (unsigned long long)map_{index}.m_llen, (unsigned long long)map_{index}.m_plen, map_{index}.m_deviceid, map_{index}.m_flags, error, gate_acquires, gate_releases); ''' ) zmap_preamble = common + buffer_compat + r''' #define EROFS_INODE_COMPRESSED_FULL 1 #define EROFS_MAP_MAPPED 0x0001 #define EROFS_MAP_META 0x0002 #define EROFS_MAP_PARTIAL_MAPPED 0x0004 #define EROFS_MAP_PARTIAL_REF 0x0008 #define EROFS_MAP_FRAGMENT 0x0010 #define EROFS_MAP_FULL(f) (!((f) & (EROFS_MAP_PARTIAL_MAPPED | EROFS_MAP_PARTIAL_REF))) #define EROFS_NULL_ADDR UINT64_MAX #define Z_EROFS_COMPRESSION_LZ4 0 #define Z_EROFS_COMPRESSION_MAX 4 #define Z_EROFS_COMPRESSION_SHIFTED 4 #define Z_EROFS_COMPRESSION_INTERLACED 5 #define Z_EROFS_COMPRESSION_RUNTIME_MAX 6 #define Z_EROFS_PCLUSTER_MAX_SIZE (1024 * 1024) #define Z_EROFS_PCLUSTER_MAX_DSIZE (12 * 1024 * 1024) #define Z_EROFS_ADVISE_EXTENTS 0x0001 #define Z_EROFS_ADVISE_INTERLACED_PCLUSTER 0x0010 #define Z_EROFS_ADVISE_FRAGMENT_PCLUSTER 0x0020 #define Z_EROFS_ADVISE_EXTRECSZ_BIT 1 #define Z_EROFS_ADVISE_EXTRECSZ_MASK 0x3 #define Z_EROFS_EXTENT_PLEN_PARTIAL (1U << 27) #define Z_EROFS_EXTENT_PLEN_FMT_BIT 28 #define Z_EROFS_EXTENT_PLEN_MASK ((Z_EROFS_PCLUSTER_MAX_SIZE << 1) - 1) struct z_erofs_extent { uint32_t plen, pstart_lo, pstart_hi, lstart_lo, lstart_hi; uint8_t reserved[12]; } __attribute__((packed)); struct z_erofs_map_header { uint32_t word0; uint16_t h_advise; uint8_t h_algorithmtype; uint8_t h_clusterbits; } __attribute__((packed)); struct erofs_map_blocks { erofs_off_t m_pa, m_la; uint64_t m_plen, m_llen; unsigned short m_deviceid; char m_algorithmformat; unsigned int m_flags; }; struct erofs_inode { erofs_nid_t nid; uint64_t size; erofs_off_t inode_off; uint8_t datalayout, inode_isize; uint32_t xattr_isize; uint16_t z_advise; uint8_t z_algorithmtype[2], z_lclusterbits; uint16_t z_idata_size; erofs_off_t z_fragmentoff; uint64_t z_tailextent_headlcn, z_extents; bool z_initialized, fragment; }; struct erofs_sb_info { uint32_t block_size; uint8_t blkszbits; uint16_t available_compr_algs; uint64_t packed_nid; struct erofs_inode *packed_inode; }; ''' zmap_stubs = r''' static int z_erofs_fill_inode(struct erofs_sb_info *sbi, struct erofs_inode *vi) { (void)sbi; return (vi->z_initialized ? 0 : EINTEGRITY); } static int z_erofs_map_blocks_fo(struct erofs_sb_info *sbi, struct erofs_inode *vi, struct erofs_map_blocks *map, int flags) { (void)sbi; (void)vi; (void)map; (void)flags; return (EOPNOTSUPP); } ''' zmap_names = [ "z_erofs_extent_add", "z_erofs_extent_roundup", "z_erofs_extent_table_pos", "z_erofs_extent_record_pos", "z_erofs_read_extent", "z_erofs_extent_lstart", "z_erofs_map_blocks_ext", "z_erofs_map_sanity_check", "z_erofs_map_blocks_iter", ] zmap_program = zmap_preamble + reader_definition(object_mode) + zmap_stubs zmap_program += extract_function( sources["repo-pre-15/src/erofs_fs.h"], "z_erofs_extent_recsize" ) for name in zmap_names: zmap_program += extract_function(sources["repo-pre-15/src/zmap.c"], name) zmap_program += "int main(void)\n{\n\tint error;\n" zmap_program += "".join(zmap_case_code) zmap_program += "\treturn (0);\n}\n" helper_result: dict[str, object] = {"status": "NOT_APPLICABLE"} if mode == "worktree": helper_program = common + r''' #define EROFS_DIRENT_NID_METABOX (1ULL << 63) typedef int64_t off_t; struct erofs_inode { uint64_t size; }; struct erofs_sb_info { struct erofs_inode *metabox_en; }; struct erofs_buf { void *data; void (*release)(void *); }; #define EROFS_BUF_INITIALIZER { .data = NULL, .release = NULL } static int gate_error; static unsigned int gate_acquires; static unsigned int gate_releases; static unsigned int gate_backend; static bool erofs_nid_in_metabox(erofs_nid_t nid) { return ((nid & EROFS_DIRENT_NID_METABOX) != 0); } static bool erofs_sb_has_metabox(struct erofs_sb_info *sbi) { return (sbi->metabox_en != NULL); } static int gate_acquire(size_t len, void **bufp, unsigned int backend) { void *data; if (gate_error != 0) return (gate_error); data = malloc(len == 0 ? 1 : len); if (data == NULL) return (EIO); *bufp = data; ++gate_acquires; gate_backend = backend; return (0); } static int erofs_bread(struct erofs_sb_info *sbi, erofs_off_t off, size_t len, void **bufp) { (void)sbi; (void)off; return (gate_acquire(len, bufp, 1)); } static int erofs_read_data(struct erofs_sb_info *sbi, struct erofs_inode *vi, erofs_off_t off, size_t len, void **bufp) { (void)sbi; (void)vi; (void)off; return (gate_acquire(len, bufp, 2)); } static void erofs_brelse(void *data) { if (data != NULL) { ++gate_releases; free(data); } } ''' helper_program += extract_function( sources["repo-pre-15/src/data.c"], "erofs_put_metabuf" ) helper_program += extract_function( sources["repo-pre-15/src/data.c"], "erofs_read_metadata" ) helper_program += r''' int main(void) { struct erofs_inode metabox = { .size = 4096 }; struct erofs_sb_info sbi = { .metabox_en = &metabox }; struct erofs_buf buf = EROFS_BUF_INITIALIZER; int error; int marker; gate_error = 0; gate_acquires = gate_releases = gate_backend = 0; error = erofs_read_metadata(&sbi, 1, 32, 8, &buf); if (error != 0 || buf.data == NULL || buf.release == NULL || gate_acquires != 1 || gate_backend != 1) return (10); erofs_put_metabuf(&buf); erofs_put_metabuf(&buf); if (buf.data != NULL || buf.release != NULL || gate_releases != 1) return (11); printf("helper\tprimary-success\t1\t1\n"); buf = (struct erofs_buf)EROFS_BUF_INITIALIZER; gate_error = 0; gate_acquires = gate_releases = gate_backend = 0; error = erofs_read_metadata(&sbi, EROFS_DIRENT_NID_METABOX | 2, 64, 8, &buf); if (error != 0 || buf.data == NULL || buf.release == NULL || gate_acquires != 1 || gate_backend != 2) return (12); erofs_put_metabuf(&buf); if (buf.data != NULL || buf.release != NULL || gate_releases != 1) return (13); printf("helper\tmetabox-success\t1\t1\n"); buf = (struct erofs_buf)EROFS_BUF_INITIALIZER; gate_error = EIO; gate_acquires = gate_releases = gate_backend = 0; error = erofs_read_metadata(&sbi, 1, 32, 8, &buf); if (error != EIO || buf.data != NULL || buf.release != NULL || gate_acquires != 0 || gate_releases != 0) return (14); printf("helper\tprovider-error\t0\t0\n"); buf = (struct erofs_buf)EROFS_BUF_INITIALIZER; gate_error = 0; gate_acquires = gate_releases = gate_backend = 0; error = erofs_read_metadata(&sbi, 1, UINT64_MAX, 8, &buf); if (error != EOVERFLOW || buf.data != NULL || buf.release != NULL || gate_acquires != 0 || gate_releases != 0) return (15); printf("helper\toffset-overflow\t0\t0\n"); buf.data = ▮ buf.release = NULL; erofs_put_metabuf(&buf); erofs_put_metabuf(NULL); if (buf.data != NULL || buf.release != NULL) return (16); printf("helper\tnull-release\t0\t0\n"); return (0); } ''' helper_lines = compile_helper("P15-005-buffer-helper", helper_program) expected_helper_lines = [ "helper\tprimary-success\t1\t1", "helper\tmetabox-success\t1\t1", "helper\tprovider-error\t0\t0", "helper\toffset-overflow\t0\t0", "helper\tnull-release\t0\t0", ] if helper_lines != expected_helper_lines: ownership_failures.append( { "field": "helper-paths", "expected": expected_helper_lines, "actual": helper_lines, } ) helper_result = { "status": "PASS" if helper_lines == expected_helper_lines else "FAIL", "paths": helper_lines, } actual_records = compile_and_run("P15-005-data-extractor", data_program) actual_records.extend(compile_and_run("P15-005-zmap-extractor", zmap_program)) actual_by_id = {record["id"]: record for record in actual_records} expected_by_id = {case["id"]: case["expected"] for case in spec["tuple_cases"]} if set(actual_by_id) != set(expected_by_id): raise SystemExit("extractor case set mismatch") tuple_failures = [] for case in spec["tuple_cases"]: actual = actual_by_id[case["id"]] expected = case["expected"] for field in spec["tuple_fields"]: if actual[field] != expected[field]: tuple_failures.append( { "id": case["id"], "field": field, "expected": expected[field], "actual": actual[field], } ) coverage = sorted( {case["category"] for case in spec["tuple_cases"] if not case.get("supplemental")} ) if set(coverage) != set(spec["required_categories"]): raise SystemExit("required tuple category coverage is incomplete") for case in spec["tuple_cases"]: actual = actual_by_id[case["id"]] if actual["acquire_count"] != actual["release_count"]: tuple_failures.append({"id": case["id"], "field": "ownership"}) if ( actual["errno"] == 0 and case["request"] < case["inode"]["size"] and actual["m_llen"] == 0 ): tuple_failures.append({"id": case["id"], "field": "H07-positive-run"}) ownership_paths = spec["ownership_paths"] consumers = sorted({path["consumer"] for path in ownership_paths}) required_consumers = ["compressed", "inode", "map", "plain", "super", "xattr"] if consumers != required_consumers: raise SystemExit(f"ownership consumer coverage mismatch: {consumers}") for path in ownership_paths: if path["function"] not in function_bodies: raise SystemExit(f"ownership path has no frozen function body: {path['id']}") if path["acquire_count"] != path["release_count"]: raise SystemExit(f"incomplete ownership oracle path: {path['id']}") ownership_result = { "status": "PASS" if not ownership_failures else "FAIL", "consumers": consumers, "direct_metadata_functions": sorted(direct_sites), "paths": ownership_paths, "baseline_body_sha256_verified": mode == "base", "linux_semantic_anchors_verified": True, "candidate_contract": candidate_contract_result, "helper_paths": helper_result, "failures": ownership_failures, "short_read_rule": "a failed read before returned storage has acquire=release=0", "success_rule": "each returned metadata allocation has exactly one release", } tuple_result = { "status": "PASS" if not tuple_failures else "FAIL", "coverage": coverage, "tuple_fields": spec["tuple_fields"], "records": [ { **actual_by_id[case["id"]], "category": case["category"], "supplemental": bool(case.get("supplemental")), } for case in spec["tuple_cases"] ], "failures": tuple_failures, "authoritative_sources": [ "compiled function bodies extracted from the frozen/current FreeBSD source", "independently frozen expected fields from EROFS records and Linux map semantics", ], } (output / "tuples.json").write_text( json.dumps(tuple_result, indent=2, sort_keys=True) + "\n", encoding="ascii" ) (output / "ownership.json").write_text( json.dumps(ownership_result, indent=2, sort_keys=True) + "\n", encoding="ascii" ) (output / "source-sha256.json").write_text( json.dumps(source_hashes, indent=2, sort_keys=True) + "\n", encoding="ascii" ) oracle_equal = None if oracle_path is not None: oracle = json.loads(oracle_path.read_text(encoding="ascii")) oracle_records = {record["id"]: record for record in oracle["records"]} oracle_equal = True for case in spec["tuple_cases"]: current = actual_by_id[case["id"]] frozen = oracle_records.get(case["id"]) if frozen is None or any(current[field] != frozen[field] for field in spec["tuple_fields"]): oracle_equal = False break if not oracle_equal: tuple_failures.append({"field": "frozen-baseline-replay"}) status = "GO" if not tuple_failures and not ownership_failures else "STOP" result = { "schema": 1, "gate": spec["gate"], "candidate": spec["candidate"], "status": status, "mode": mode, "requested_base": requested_base or None, "resolved_head": resolved, "object_mode": object_mode, "tuple_status": tuple_result["status"], "ownership_status": ownership_result["status"], "coverage": coverage, "case_count": len(actual_records), "ownership_path_count": len(ownership_paths), "oracle_equal": oracle_equal, "qemu": "NOT_RUN", "full_feature_suite": "NOT_RUN", "failures": tuple_failures + ownership_failures, } (output / "result.json").write_text( json.dumps(result, indent=2, sort_keys=True) + "\n", encoding="ascii" ) hashes = [] for path in sorted(output.iterdir()): if path.is_file() and path.name != "SHA256SUMS": hashes.append(f"{hashlib.sha256(path.read_bytes()).hexdigest()} {path.name}") (output / "SHA256SUMS").write_text("\n".join(hashes) + "\n", encoding="ascii") print(json.dumps(result, sort_keys=True)) if status != "GO": raise SystemExit(1) PY