phase3: generalize trust layer for C/C++; wire cJSON target

Trust-layer generalization (zero-regression verified on tokei):
- TargetSpec [build] uses an explicit {out} isolation-dir placeholder
  instead of injected CARGO_TARGET_DIR, plus optional sanitizer/
  sanitizer_binary templates. Nothing assumes cargo anymore.
- verdict rebuild_pristine_baseline, sanitizer_check, and harnessd
  run_verdict candidate build all use {out} substitution. A target with
  no sanitizer build caps accepts at needs-human-review.
- tokei/comrak target.toml migrated to the {out} form (CARGO_TARGET_DIR=
  {out} + nightly ASan sanitizer command).

cJSON C target (v1.7.19, MIT):
- harness.c bench/golden driver (trust-layer; agent can patch workspace
  cJSON.c but not the rig), compiled with clang against the patched tree.
- 5.6 MB deterministic corpus (gen-corpus.sh) + MANIFEST/GOLDEN pins.
- gates = hermetic Unity/CTest suite (19 tests pass); sanitizer =
  ASan+UBSan; scripts/diff-fuzz-cjson.py for differential fuzzing.

coz causal profiler integration (SPEC §12):
- harness.c optional -DHOTPATH_COZ throughput progress point.
- scripts/install-coz.sh, coz-profile.sh, coz-summary.py; just coz/install-coz.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EcKPWQ3MQD4NdEHwar96u2
This commit is contained in:
Claude
2026-07-11 18:17:26 +00:00
parent 7f67c3bb02
commit 014e3b11d1
17 changed files with 528 additions and 58 deletions
+41
View File
@@ -0,0 +1,41 @@
#!/bin/sh
# coz causal profiling for a C/C++ target (SPEC §12). Unlike a flat
# profiler (callgrind ranks where time is SPENT), coz measures where a
# speedup would actually RAISE throughput — it virtually speeds up each
# line and reports the causal effect on the harness's progress points.
# Use it to rank playbook targets when the flat profile is ambiguous.
#
# scripts/coz-profile.sh cjson [iters]
#
# Requires: coz on PATH (build: git clone github.com/plasma-umass/coz &&
# see scripts/install-coz.sh) and the target's harness built with
# -DHOTPATH_COZ (this script does that). Output: results/<target>/coz/
set -eu
ROOT=$(cd "$(dirname "$0")/.." && pwd)
TARGET=${1:?usage: coz-profile.sh <target> [iters]}
ITERS=${2:-200}
OUT="$ROOT/results/$TARGET/coz"
mkdir -p "$OUT"
command -v coz >/dev/null 2>&1 || {
echo "coz not on PATH — run scripts/install-coz.sh first" >&2
exit 1
}
# coz needs debug line info and the progress point compiled in.
BIN="$OUT/cjson-bench-coz"
clang -O2 -g -gdwarf-4 -DHOTPATH_COZ \
-I "$ROOT/targets/$TARGET/workspace" \
-I "$(dirname "$(command -v coz)")/../include" \
-o "$BIN" \
"$ROOT/targets/$TARGET/harness.c" \
"$ROOT/targets/$TARGET/workspace/cJSON.c" \
-ldl
cd "$ROOT"
coz run --output "$OUT/profile.coz" --- \
"$BIN" corpora/$TARGET/input/big.json "$ITERS" >/dev/null
echo "coz profile written: $OUT/profile.coz"
echo "view at https://plasma-umass.org/coz/ (load the .coz file) or:"
echo " python3 scripts/coz-summary.py $OUT/profile.coz"
+75
View File
@@ -0,0 +1,75 @@
#!/usr/bin/env python3
"""Summarize a coz causal profile: rank source lines by their estimated
causal effect on throughput. coz writes newline-delimited JSON records;
for each (line, virtual-speedup) it records progress-point throughput.
We fit the slope of throughput-gain vs virtual-speedup per line — a
steeper positive slope means "speeding up this line most raises overall
throughput", which is exactly the class-selection signal a flat profiler
can't give.
python3 scripts/coz-summary.py results/<target>/coz/profile.coz
"""
from __future__ import annotations
import collections
import json
import sys
from pathlib import Path
def main() -> int:
path = Path(sys.argv[1]) if len(sys.argv) > 1 else None
if not path or not path.exists():
print("usage: coz-summary.py <profile.coz>", file=sys.stderr)
return 2
# experiments: line -> list of (speedup, progress-rate)
exps: dict[str, list[tuple[float, float]]] = collections.defaultdict(list)
baseline: dict[str, float] = {}
for raw in path.read_text().splitlines():
raw = raw.strip()
if not raw:
continue
try:
rec = json.loads(raw)
except json.JSONDecodeError:
continue
if rec.get("type") != "experiment":
continue
line = rec.get("selected", "?")
speedup = float(rec.get("speedup", 0.0))
duration = float(rec.get("duration", 0.0))
delta = float(rec.get("delta", 0.0))
if delta <= 0:
continue
rate = duration / delta # time per progress unit; lower = faster
exps[line].append((speedup, rate))
if speedup == 0.0:
baseline[line] = rate
rows = []
for line, pts in exps.items():
base = baseline.get(line)
if not base or len(pts) < 2:
continue
# % throughput improvement at the max virtual speedup tried
pts.sort()
s_max, r_max = pts[-1]
if s_max <= 0 or r_max <= 0:
continue
gain = (base - r_max) / base # fractional throughput gain
rows.append((gain, s_max, line))
rows.sort(reverse=True)
print(f"{'throughput_gain':>16} {'@speedup':>9} line")
print("-" * 70)
for gain, s_max, line in rows[:25]:
print(f"{gain * 100:>14.1f}% {s_max * 100:>7.0f}% {line}")
if not rows:
print("(no usable experiments — did the run mark progress points?)")
return 0
if __name__ == "__main__":
sys.exit(main())
+100
View File
@@ -0,0 +1,100 @@
#!/usr/bin/env python3
"""Differential fuzz for cjson attempts: pristine vs candidate harness must
produce byte-identical stdout (serialization + checksum) AND identical exit
code on mutated JSON. cJSON is a deterministic single-threaded parser, so
raw byte comparison needs no canonicalization.
python3 scripts/diff-fuzz-cjson.py <workdir> <iters> <candidate-bin>
Seeds: slices of the pinned corpus plus hand JSON fragments. Mutations
target parser edges: structural tokens, string escapes, number forms
(leading zeros, +/- exponents, huge/deep values), and raw byte noise.
"""
import random
import subprocess
import sys
from pathlib import Path
ROOT = Path("/home/user/peltier.io")
BASE = ROOT / "targets/cjson/baseline/cjson-bench"
CAND = ROOT / sys.argv[3] if len(sys.argv) > 3 else ROOT / "targets/cjson/baseline/cjson-bench"
CORPUS = ROOT / "corpora/cjson/input/big.json"
WORK = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("/tmp/difffuzz-cjson")
ITERS = int(sys.argv[2]) if len(sys.argv) > 2 else 10000
TOKENS = [
b"{", b"}", b"[", b"]", b'"', b":", b",", b"\\", b"\\n", b"\\u00e9", b"\\uD83D",
b"true", b"false", b"null", b"0", b"-0", b"1e999", b"-1.5E-3", b"00", b"1.",
b".5", b"1e", b"NaN", b"Infinity", b'\\"', b'"key"', b"[]", b"{}", b'""',
b"1234567890123456789012345", b"\x00", b"\xff", b"\t", b"\n",
]
SEEDS_STATIC = [
b'{}', b'[]', b'{"a":1}', b'[1,2,3]', b'"hello"', b'true', b'null',
b'{"n":-1.5e10,"s":"a\\tb","u":"\\u00e9","arr":[null,false,{}]}',
b'[[[[[[1]]]]]]', b'{"a":{"b":{"c":{"d":1}}}}', b'123.456e-78',
b'{"esc":"\\"\\\\\\/\\b\\f\\n\\r\\t"}', b' { "x" : [ 1 , 2 ] } ',
]
def mutate(data: bytes, rng: random.Random) -> bytes:
b = bytearray(data)
for _ in range(rng.randrange(1, 8)):
if not b:
b = bytearray(rng.choice(TOKENS))
continue
op = rng.randrange(5)
pos = rng.randrange(len(b) + 1)
if op == 0:
b[pos:pos] = rng.choice(TOKENS)
elif op == 1 and len(b) > 2:
del b[pos:min(len(b), pos + rng.randrange(1, 12))]
elif op == 2 and pos < len(b):
b[pos] = rng.randrange(256)
elif op == 3:
end = min(len(b), pos + rng.randrange(1, 48))
b[end:end] = b[pos:end]
else:
b[pos:pos] = rng.choice(TOKENS)
return bytes(b)
def run(binary: Path, path: Path) -> tuple[bytes, int]:
r = subprocess.run([str(binary), str(path), "1"], capture_output=True, timeout=30)
return r.stdout, r.returncode
def main() -> int:
rng = random.Random(0x1CE_B00C)
corpus = CORPUS.read_bytes()
seeds = list(SEEDS_STATIC)
# windows of the real corpus (often invalid mid-slice — exercises the
# error path, which must diverge identically or not at all)
for o in range(0, len(corpus) - 4096, len(corpus) // 200):
for n in (64, 512, 4096):
seeds.append(corpus[o : o + n])
WORK.mkdir(parents=True, exist_ok=True)
case = WORK / "case.json"
divergences = 0
for i in range(ITERS):
payload = mutate(rng.choice(seeds), rng)
case.write_bytes(payload)
a_out, a_rc = run(BASE, case)
b_out, b_rc = run(CAND, case)
if a_out != b_out or a_rc != b_rc:
divergences += 1
keep = WORK / f"divergence-{i}"
keep.mkdir(exist_ok=True)
(keep / "input.json").write_bytes(payload)
(keep / "baseline.out").write_bytes(a_out + f"\nrc={a_rc}".encode())
(keep / "candidate.out").write_bytes(b_out + f"\nrc={b_rc}".encode())
print(f"DIVERGENCE at iter {i} (rc {a_rc} vs {b_rc}); kept in {keep}")
if (i + 1) % 1000 == 0:
print(f"{i + 1}/{ITERS}, {divergences} divergences", flush=True)
print(f"done: {ITERS} inputs, {divergences} divergences")
return 1 if divergences else 0
if __name__ == "__main__":
sys.exit(main())
+28
View File
@@ -0,0 +1,28 @@
#!/bin/sh
# Install the coz causal profiler (SPEC §12). Tries the distro package
# first, then a source build. Idempotent — skips if `coz` is already on
# PATH. Run as a user that can apt-install (root in the dev container).
set -eu
if command -v coz >/dev/null 2>&1; then
echo "coz already installed: $(command -v coz)"
exit 0
fi
echo "installing coz…"
if apt-get install -y coz-profiler >/dev/null 2>&1; then
echo "installed via apt: $(command -v coz)"
exit 0
fi
# Source build fallback.
apt-get install -y python3 libelf-dev libdwarf-dev build-essential git >/dev/null 2>&1 || true
SRC=/opt/coz-src
rm -rf "$SRC"
git clone --depth 1 https://github.com/plasma-umass/coz.git "$SRC"
make -C "$SRC" >/dev/null
make -C "$SRC" install >/dev/null
command -v coz >/dev/null 2>&1 && echo "installed from source: $(command -v coz)" || {
echo "coz install failed" >&2
exit 1
}