Skip to content

Repository files navigation

kharness

kharness is a local-first MVP harness for authorized KernelCTF and local-lab kernel exploitability triage.

Core behavior

  • Cases are isolated in kharness/cases/<case-id>/.
  • Facts are stored as append-only JSONL in evidence.jsonl.
  • Trace events are stored as append-only JSONL in trace.jsonl.
  • Runtime claims are derived from typed schemas and replayable artifacts.
  • target import-kctf defaults to the current KernelCTF latest-LTS runtime profile, not raw .config reachability.
  • The single autonomous entrypoint is kharness run: it can refresh scout recon/plan context, execute bounded scout workers, then run the deep worker loop that chooses source/artifact/scout/probe actions.

KernelCTF Environment Profile

The latest-LTS profile follows the public KernelCTF server rules and qemu.sh shape:

  • unprivileged user namespaces disabled with sysctl.user.max_user_namespaces=1
  • io_uring disabled with sysctl.kernel.io_uring_disabled=2
  • nftables treated as unavailable
  • unprivileged BPF treated as unavailable
  • post-2025 BPF JIT hardening enabled with sysctl.net.core.bpf_jit_harden=2
  • QEMU profile records 3.5G RAM and 2 vCPUs

Local kenv cpio workspaces still differ from the live KernelCTF environment in one important way: they do not model the read-only ext4 rootfs_v3.img plus ramdisk_v1.img boot flow. Treat page-cache, setuid-binary, mount, and flag-drive behavior as unproven until tested against a KernelCTF-style ext4 rootfs or the live target.

If you have a rootfs_v3.img-style raw disk, import it with:

PYTHONPATH=src python3 -m kharness.cli target import-kctf \
  --name lts-6.12.95 \
  --bzimage /path/to/bzImage \
  --config /path/to/.config \
  --rootfs /path/to/rootfs_v3.img \
  --rootfs-kind kernelctf-ext4-rootfs_v3.img \
  --uid 1000

Disk-mode probe execution is supported through kharness probe qemu-run. Probes build with a local compiler, then qemu-run overlays the probe into a copy of ramdisk_v1.img and boots QEMU with the read-only raw disk unchanged.

PYTHONPATH=src python3 -m kharness.cli --root /path/to/research-root probe qemu-run \
  --case <case-id> \
  --source /path/to/case/probes/condition/probe.c \
  --base-initrd /path/to/ramdisk_v1.img \
  --probe-cc /usr/bin/gcc \
  --qemu-bin /usr/bin/qemu-system-x86_64

For initramfs/cpio targets that do not run scripts/<stage> hooks, qemu-run falls back to a direct /init wrapper and records the selected hook mode in runs/<run-id>/initrd-hook.json and the QEMU result JSON.

Required tooling

  • Python 3.12+
  • A writable checkout of this repository.
  • kenv for local kernel workspace execution (optional but supported).

Codex runtime

Default model provider is the local Codex executor:

  • provider: codex_exec
  • model: x-preview-f-free
  • reasoning effort: xhigh
  • executor: bin/codex-free --yolo exec (configurable Codex profile)
  • default autonomous profile: run
  • stage budgets: run, critic, probe_generator, reflection, summary, and delta_context

Deep-worker lanes run as one stateful codex exec --json turn. Kharness is attached as a required stdio MCP server, so source reads, artifact reads, candidate changes, generated probes, QEMU runs, judgements, and finish events remain artifact-backed while Codex keeps its plan and reasoning context across tool calls. A generated worker hook blocks shell, file-edit, and subagent tools, and web search is disabled, preventing those paths from bypassing the ledger. Codex automatic compaction handles long turns.

export KHARNESS_CODEX_BIN="$PWD/bin/codex-free"
export KHARNESS_CODEX_ARGS="--yolo"
export KHARNESS_CODEX_MODEL=x-preview-f-free
export KHARNESS_CODEX_REASONING_EFFORT=xhigh

Override defaults without editing code:

export KHARNESS_RUN_REASONING_EFFORT="xhigh"
export KHARNESS_RUN_TIMEOUT=1800
export KHARNESS_RUN_BACKGROUND=false

Each run records the Codex thread ID, sanitized JSON event stream, usage, compaction count, MCP calls, transcript, and final response. --resume-run resumes that same Codex thread with a small ledger delta rather than rebuilding the full prompt. KHARNESS_MODEL_PROVIDER and alternate API providers were removed; setting the old provider variable is an explicit configuration error.

When kharness run uses --budget-profile auto, kharness selects the run profile so the LLM has enough room to choose strategy before the harness narrows anything.

source index remains available as source inventory/context for model runs. It is not a ranking or pre-filtering policy.

Quick start

The live target is lts-6.12.95. Its payout slot is already occupied; use it for current-target validation while preparing lts-6.12.96, scheduled to go live on 2026-08-07. Recheck the live server before beginning a campaign rather than relying on the lagging public releases.yaml.

PYTHONPATH=src python3 -m kharness.cli target import-kctf \
  --name kernelctf-lts-6.12.95 \
  --bzimage /path/to/assets/kernelctf/releases/lts-6.12.95/bzImage \
  --config /path/to/assets/kernelctf/releases/lts-6.12.95/.config \
  --rootfs /path/to/orig/rootfs.cpio \
  --uid 1000

PYTHONPATH=src python3 -m kharness.cli surface map --case kernelctf-lts-6.12.95-default-000000 --run-probe
PYTHONPATH=src python3 -m kharness.cli --root /path/to/research-root scout recon \
  --case kernelctf-lts-6.12.95-default-000000 \
  --source-root /path/to/linux-6.12.95 \
  --path net --path fs --path drivers
PYTHONPATH=src python3 -m kharness.cli --root /path/to/research-root run \
  --case kernelctf-lts-6.12.95-default-000000 \
  --source-root /path/to/linux-6.12.95 \
  --base-initrd /path/to/ramdisk_v1.img \
  --scout-max-tasks 200 \
  --parallel 50 \
  --qemu-parallel 2
PYTHONPATH=src python3 -m kharness.cli --root /path/to/research-root judge qemu \
  --case kernelctf-lts-6.12.95-default-000000
PYTHONPATH=src python3 -m kharness.cli --root /path/to/research-root probe next \
  --case kernelctf-lts-6.12.95-default-000000
PYTHONPATH=src python3 -m kharness.cli score --case kernelctf-lts-6.12.95-default-000000
PYTHONPATH=src python3 -m kharness.cli replay --case kernelctf-lts-6.12.95-default-000000

probe qemu-run automatically writes judge.result.json and primitive-ladder.result.json beside the QEMU result, plus durable case-level entries under judgements/ and primitive_ladders/.

CLI commands

  • kharness target import-kctf ...
  • kharness surface map --case <case-id> [--run-probe]
  • kharness run --case <case-id> --source-root <linux-tree> [--base-initrd <ramdisk_v1.img>]
    • Stateful Codex workers have no harness-imposed model/tool-call ceiling by default.
    • --max-steps <n> sets an optional finite budget; --max-steps 0 is unlimited. With --resume-run, a positive value appends up to <n> more steps after the existing transcript.
    • --parallel <n> is the harness-wide fanout policy. It applies to scout execution, deep-worker lane scheduling, and read-only action fanout. Use --model-parallel, --io-parallel, and --qemu-parallel only as resource caps.
    • --scout-max-tasks <n> executes that many narrow scout tasks before the deep worker. Use --scout-only to stop after scout artifacts are written.
    • With --parallel > 1, kharness run writes scheduler_runs/<id>/scheduler-run.result.json and launches independent worker lanes from unvalidated probe plans and scout measurement questions.
    • Run prompts include generic loop-health context from the transcript, such as source-only streaks, repeated source paths, recent source retirements, and recent probe/QEMU activity. This nudges the model to probe, finish/retire, or pivot without adding harness-owned candidate filters.
  • kharness scout recon --case <case-id> --source-root <linux-tree> [--path <relative-path>]
  • kharness scout plan --case <case-id> --source-root <linux-tree> [--max-tasks <n>]
  • kharness scout validate --case <case-id> --result <scout/runs/.../result.json>
  • kharness scout summarize --case <case-id> [--manifest <manifest-id>]
  • kharness source index --case <case-id> --source-root <linux-tree> [--path <relative-path>]
  • kharness delta mine --case <case-id> --patch <patch> [--source-root <linux-tree>] as patch inventory/context only
  • kharness probe register --case <case-id> --source <probe.c> --stage <reachability|trigger|condition|primitive>
  • kharness probe qemu-run --case <case-id> --source <probe.c> --base-initrd <ramdisk_v1.img>
  • kharness judge qemu --case <case-id> [--run-result <qemu-run.result.json>]
  • kharness probe next --case <case-id> [--judge-result <judge.json>]
  • kharness model budget --case <case-id> [--purpose run]
  • kharness eval builtin
  • kharness env preflight [--case <case-id>] [--base-initrd <ramdisk.img>] [--probe-cc <cc>] [--source-root <linux-tree>]
    • With --case, preflight records target asset, source root, base-initrd, QEMU, and local compiler readiness in env_preflight.json, including case_setup_blocked failure subtypes when local QEMU cannot run.
  • kharness replay --case <case-id>
  • kharness score --case <case-id>

About

automated kernel bug hunting harness

Topics

Resources

Stars

4 stars

Watchers

0 watching

Forks

Contributors

Languages