Download scripts/dynamic_validate_confirmed.py from AProver/aprover: direct link, hf CLI and curl.
- Browser
- Download file 6.18 kB
-
https://huggingface.co/spaces/AProver/aprover/resolve/main/scripts/dynamic_validate_confirmed.py
- Command line
-
hf download hf://spaces/AProver/aprover/scripts/dynamic_validate_confirmed.py
-
curl -L -o dynamic_validate_confirmed.py https://huggingface.co/spaces/AProver/aprover/resolve/main/scripts/dynamic_validate_confirmed.py
6.18 kB
| """ | |
| Retroactively run dynamic validation on every BMC-confirmed adjacent bug | |
| in a judge-dir output directory. | |
| For each confirmed bug: | |
| 1. Re-parse the target function's source file (so we have FunctionInfo) | |
| 2. Ask the LLM (gpt-5 by default) to produce a C reproducer using | |
| libarchive's public API + the attacker_scenario the judge produced | |
| 3. Compile with gcc -fsanitize=address,undefined linked against the | |
| built libarchive .so | |
| 4. Run with timeout; record signal / sanitizer output if any | |
| 5. Persist the outcome alongside the original judge_*.json record | |
| Usage: | |
| OPENROUTER_API_KEY=... \ | |
| BMC_AGENT_LLM_MODEL=openai/gpt-5 \ | |
| python scripts/dynamic_validate_confirmed.py <judge_output_dir> | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import os | |
| import sys | |
| from pathlib import Path | |
| REPO = Path(__file__).resolve().parent.parent | |
| sys.path.insert(0, str(REPO)) | |
| from bmc_agent.config import Config | |
| from bmc_agent.llm import LLMClient | |
| from bmc_agent.parser import parse_c_file | |
| from bmc_agent.preprocessor import preprocess | |
| from bmc_agent.judge_pipeline import ( | |
| _dynamic_validate_bug, | |
| _DEFAULT_LIBARCHIVE_BUILD, | |
| _DEFAULT_LIBARCHIVE_INC, | |
| ) | |
| CORPUS = Path("/tmp/libarchive_seedhunt_full") | |
| BUILD_INC = "/tmp/libarchive_bench/libarchive/build" | |
| LIBA_INC = "/tmp/libarchive_bench/libarchive/libarchive" | |
| def main(): | |
| if len(sys.argv) < 2: | |
| print(__doc__) | |
| return 1 | |
| out_root = Path(sys.argv[1]) | |
| if not out_root.is_dir(): | |
| print(f"ERROR: not a dir: {out_root}") | |
| return 1 | |
| # OpenAI env wiring (route via OpenRouter) | |
| if os.environ.get("OPENROUTER_API_KEY") and not os.environ.get("BMC_AGENT_LLM_API_KEY"): | |
| os.environ["BMC_AGENT_LLM_API_KEY"] = os.environ["OPENROUTER_API_KEY"] | |
| os.environ.setdefault("BMC_AGENT_LLM_BASE_URL", "https://openrouter.ai/api/v1") | |
| os.environ.setdefault("BMC_AGENT_LLM_PROVIDER", "openai") | |
| os.environ.setdefault("BMC_AGENT_LLM_MODEL", "openai/gpt-5") | |
| config = Config.from_env() | |
| config.llm_request_timeout_s = 600.0 | |
| llm = LLMClient(config) | |
| # Cache parsed files | |
| parsed_cache: dict[str, "ParsedCFile"] = {} | |
| def _get_parsed(stem: str): | |
| path = CORPUS / f"{stem}.c" | |
| if not path.exists(): | |
| return None | |
| if path.as_posix() not in parsed_cache: | |
| try: | |
| expanded = preprocess( | |
| path, include_dirs=[BUILD_INC, LIBA_INC], | |
| defines=["HAVE_CONFIG_H"], | |
| ) | |
| parsed_cache[path.as_posix()] = parse_c_file(path, source_text=expanded) | |
| except Exception as exc: | |
| print(f" parse failed for {path}: {exc}") | |
| return None | |
| return parsed_cache[path.as_posix()] | |
| # Walk judge_*.json, find confirmed adjacents | |
| summary = [] | |
| for jf in sorted(out_root.rglob("judge_*.json")): | |
| try: | |
| d = json.load(open(jf)) | |
| except Exception: | |
| continue | |
| src_fn = jf.parent.name | |
| stem = jf.parts[-3] # file_stem | |
| confirmations = d.get("adjacent_confirmations") or [] | |
| any_confirmed = any(c.get("confirmed") for c in confirmations) | |
| if not any_confirmed: | |
| continue | |
| parsed = _get_parsed(stem) | |
| if parsed is None: | |
| print(f"\n=== SKIP {jf} (no parsed file for stem {stem}) ===") | |
| continue | |
| for idx, cf in enumerate(confirmations): | |
| if not cf.get("confirmed"): | |
| continue | |
| tgt = cf.get("target_function") | |
| if not tgt or tgt not in parsed.functions: | |
| print(f" skip: target {tgt!r} not in parsed file") | |
| continue | |
| target_func = parsed.get_function_info(tgt) | |
| adj = cf.get("adjacent") or {} | |
| scenario = ( | |
| cf.get("attacker_scenario") | |
| or adj.get("attacker_scenario") | |
| or "" | |
| ) | |
| print(f"\n=== Dynamic-validating {stem}/{src_fn} → {tgt} ===") | |
| print(f" bug_type: {(adj.get('bug_type') or '')[:90]}") | |
| out_dir = jf.parent / "dynamic" / tgt | |
| try: | |
| result = _dynamic_validate_bug( | |
| func=target_func, | |
| attacker_scenario=scenario, | |
| parsed_file=parsed, | |
| libarchive_build_dir=_DEFAULT_LIBARCHIVE_BUILD, | |
| libarchive_include_dir=_DEFAULT_LIBARCHIVE_INC, | |
| llm=llm, | |
| out_dir=out_dir, | |
| ) | |
| except Exception as exc: | |
| result = {"outcome": "exception", "reason": str(exc)} | |
| print(f" outcome={result.get('outcome')} signal={result.get('signal_name')}") | |
| if result.get("stderr_excerpt"): | |
| # Show first ASan/UBSan summary line if present | |
| for line in result["stderr_excerpt"].splitlines(): | |
| if "Sanitizer" in line or "runtime error" in line: | |
| print(f" !! {line[:200]}") | |
| break | |
| cf["dynamic_validation"] = result | |
| summary.append({ | |
| "source_judge": str(jf), | |
| "src_fn": src_fn, | |
| "target_fn": tgt, | |
| "bug_type": adj.get("bug_type"), | |
| "outcome": result.get("outcome"), | |
| "signal": result.get("signal_name"), | |
| }) | |
| # Persist back the updated record | |
| with open(jf, "w") as f: | |
| json.dump(d, f, indent=2) | |
| # Top-line | |
| print() | |
| print("=" * 80) | |
| print(f"Summary: {len(summary)} confirmed bugs dynamic-validated") | |
| print("=" * 80) | |
| print(f"{'src_fn':28s} {'target_fn':28s} {'outcome':22s} {'signal':10s}") | |
| print("-" * 90) | |
| counts = {} | |
| for s in summary: | |
| oc = s["outcome"] | |
| counts[oc] = counts.get(oc, 0) + 1 | |
| print(f"{s['src_fn'][:28]:28s} {(s['target_fn'] or '?')[:28]:28s} " | |
| f"{oc:22s} {(s['signal'] or '-')[:10]:10s}") | |
| print() | |
| print(f"Outcome tallies: {counts}") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |