
Evidence-driven Linux kernel vulnerability research harness used in the investigation of CVE-2026-31720
Research Tool · Original Import: 3 April 2026 · Documentation Revision: 11 July 2026
Core Philosophy — External Signal
Let reproducible observations outside model inference guide attention; never mistake priority for proof.
Project status. This repository preserves the initial version of an LLM-assisted research harness built and used for real Linux kernel vulnerability research. This version was used in the investigation that discovered the vulnerability published as CVE-2026-31720. The harness prioritizes investigation targets, but it does not automatically prove vulnerabilities or guarantee kernel security; final validation and reporting remain human responsibilities.
Abstract— When an LLM is asked to explore a codebase as large as the Linux kernel without structure, its context quickly disperses and the presence of dangerous APIs is easily confused with actual exploitability. Kernel Codex Harness frames this challenge as a problem of investigation prioritization and stateful orchestration, rather than automatic vulnerability detection. The project calls its principle of controlling model attention with reproducible observations computed outside LLM inference . It combines kernel paths, userspace boundaries, static lifetime, usercopy, refcount, and size signals with optional syzbot crash intelligence to rank candidate files and turn each candidate into a narrow prompt bundle. Manual review and the time-budgeted autopilot share the same response contract and session state. The harness was used in a real Linux kernel investigation that discovered a stack out-of-bounds write in the USB gadget audio path, later published as . This implementation is not a precise static analyzer; it is a research workflow that uses explainable heuristics to constrain the scope of LLM investigation, and every finding requires human revalidation of reachability, the invariant break, and concrete impact.
Index Terms— Linux kernel, vulnerability research, external signal, LLM orchestration, heuristic prioritization, syzbot, program analysis, Codex.
Linux kernel security review has two distinct scale problems. First, the full source tree is too large for a single LLM context. Second, signals such as copy_from_user, allocators, refcounts, and locks are common, but do not by themselves imply a vulnerability. An analyst must first decide where to look, then separately prove userspace reachability and the concrete state transition.
The core philosophy of this project is External Signal.
Do not let the LLM decide where to look on its own. Reproducible signals outside model inference allocate attention, while vulnerability conclusions are determined only by reachability and invariant evidence.
The harness therefore does not ask the model to wander broadly across the entire kernel. It prioritizes files, presents one investigation branch at a time, and requires an evidence structure before a conclusion.
External Signal is not a judgment generated by an LLM. It is an observation determined before model execution and reproducible from the same source tree, profile, and stored syzbot JSON. Path weights, regular-expression hits, and cached syzbot overlap are examples. These signals affect only candidate ranking and prompt context; they are never promoted to a verdict or proof.
In this document, External Signal names the overall project philosophy. The ExternalSignal data model in the code currently represents only the syzbot-derived subset, so the two terms have different scopes.
Regular-expression hits, high-risk paths, and syzbot overlap are all signals for ordering an investigation. A high score is not a security finding when the actual call path, privileges, kernel configuration, namespace, or device availability does not permit attacker reachability.
The audit first identifies boundaries that originate in userspace, such as a syscall, ioctl, netlink, procfs, filesystem, BPF, or driver hook. Only then does it evaluate bug classes such as UAF, OOB access, refcount errors, races, information leaks, or capability-check failures.
An investigation unit is normally limited to one file and its nearby caller, teardown, and free paths. At most two model-recommended manual follow-ups are allowed. This limit is not intended to reduce exploration capability, but to keep conclusions within a verifiable scope.
The prompt requires a strong finding to explain at least the following:
When evidence is insufficient, the model returns one next target to inspect instead of making a strong vulnerability claim. This is a prompt-level evidence contract; the current parser does not automatically prove the completeness of every evidence field. Ingestion normalizes the verdict and next target, so final evidence validation remains a human responsibility.
The initial investigation flow drew inspiration from the file-level analysis, bounded context expansion, and structured outputs used by Protect AI's vulnhuntr [1]. Rather than applying that Python application workflow unchanged, this project redesigned it around userspace-reachable kernel surfaces, kernel object lifetimes, teardown paths, and syzbot overlap. In particular, separating prioritization signals from vulnerability proof and checking reachability before bug class is the kernel harness's central design choice.
Fig. 1. The External Signal layer turns observations computed before model inference into ranked review units. It allocates attention but does not establish vulnerability proof.
TABLE I — MAJOR MODULE RESPONSIBILITIES
| Module | Responsibility |
|---|---|
targeting.py | Kernel file discovery and scoring of path, pattern, and syzbot signals |
models.py | Data models for Candidate, Signal, and the syzbot-derived ExternalSignal |
bundle.py | Manifest, session index, and prompt/snippet bundle generation |
prompting.py | Kernel audit prompts centered on reachability and invariants |
session.py | State for pending reviews, history, and follow-up depth |
ingest.py | Normalization of strict verdicts and next targets |
autopilot.py | Time-budgeted codex exec, logs, archives, and finding management |
syzbot.py | Public syzbot page collection and local JSON cache generation |
cli.py | Command routing for scan, inspect, codex, loop, and autopilot |
The scanner walks .c and .h files under the profile's include directories. Conceptually, the priority score for file f is composed as follows.
Score(f) = Σ path_weight(f)
+ Σ line_signal_weight(f)
+ Σ syzbot_overlap_weight(f)
This score is neither a probability nor an exploitability metric. Its components provide only a relative order for deciding which files the model should inspect first. The current implementation sums all line-level matches and limits only the highest-ranked signals displayed in the prompt. Reproducing the same result assumes the same source tree, profile, and cached syzbot JSON. A syzbot weight is applied after path and line heuristics have already made a file a candidate; a syzbot hit alone does not create a new candidate file.
The main static signals are:
ioctl, compat handler, file operation hookcopy_from_user, copy_to_user, __userkmalloc, kzalloc, kvmalloc, cache allocation, and free pathsThe built-in profiles are default, net, fs, io_uring, bpf, and drivers. A profile defines include paths, patterns, weights, and the number of signals retained per file. Rather than applying one scoring policy to the entire kernel, profiles reflect subsystem-specific attack surfaces and lifetime characteristics.
syzbot-fetch extracts titles, subsystems, bug types, and file:line information from public syzbot bug pages in the syzkaller project [2] and stores them in a JSON cache. Exact file overlap is used as a strong External Signal, while subsystem overlap is a weaker one. Because the live dashboard can change, the unit of reproducibility is the JSON captured at fetch time. Crash information is a starting point for variant hunting, not evidence of a new vulnerability.
scan creates a ranked candidate manifest and prompt bundles for the highest-ranked targets. Each prompt includes the target path, scoring rationale, line signals, syzbot context, and audit procedure.
Model responses are normalized to one of the following verdicts:
cve_candidateplausible_security_buglatent_bugnot_cve_candidateneeds_more_contextA response includes one Single best next target and a short summary. A stale response without a pending target is archived separately rather than attached to a new target.
git clone https://github.com/foxirain/linux-kernel-codex-harness.git
cd linux-kernel-codex-harness
python3 -m venv .venv
source .venv/bin/activate
python -m pip install .
Built-in profile JSON files are included in the wheel. External JSON rules can be supplied with --config /path/to/profile.json.
# 1. Create a ranked session.
kernel-harness scan /path/to/linux \
--profile net \
--limit 80 \
--top 20 \
--out artifacts
# 2. Inspect high-priority candidates.
kernel-harness inspect artifacts/session-YYYYMMDDTHHMMSSZ --top 10
# 3. Render one focused prompt.
kernel-harness codex artifacts/session-YYYYMMDDTHHMMSSZ \
--rank 1 \
--include-snippet
--limit is the number of candidates retained in the manifest, while --top is the number of prompt bundles pre-generated initially. Bundles for later ranks can be generated on demand.
kernel-harness autopilot artifacts/session-YYYYMMDDTHHMMSSZ \
--duration 30m \
--per-run-timeout 10m \
--include-snippet
The default sandbox is read-only. Specify --sandbox workspace-write only when file modification is strictly necessary during analysis.
kernel-harness syzbot-fetch https://syzkaller.appspot.com/upstream \
--out artifacts/syzbot/upstream.json \
--limit 50
kernel-harness scan /path/to/linux \
--profile fs \
--syzbot-json artifacts/syzbot/upstream.json \
--out artifacts
artifacts/session-<timestamp>/
├── SESSION.md
├── targets.json
├── finding_template.json
├── review_state.json
├── codex_response.txt # present while a response is pending
├── bundles/
│ ├── <rank>-<target>.md
│ └── <rank>-<target>.snippet.txt
├── responses/
└── autopilot/
├── AUTOPILOT_STATUS.txt
├── AUTOPILOT_PROGRESS.txt
├── AUTOPILOT_FINDINGS.txt
├── prompts/
├── exec/
└── findings/
This version went beyond a proof of concept and was used in a real Linux kernel vulnerability investigation.
TABLE II — DISCLOSED VULNERABILITY OUTCOME
| Public outcome | Affected area | Severity / CVSS | Vulnerability | Investigation model |
|---|---|---|---|---|
| CVE-2026-31720 | USB gadget audio · drivers/usb/gadget/function/f_uac1_legacy.c | Host-controlled request length could overflow a four-byte stack object | Finding surfaced during a v1-assisted investigation; validation and disclosure remained human-led |
CVE-2026-31720: NVD CVSS 3.1 · 7.8 High · CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:HVerification focuses on implementation regressions and distributability, not a detection-accuracy benchmark.
TABLE III — ENGINEERING VERIFICATION SCOPE
| Verification item | Expected property |
|---|---|
| Allocator regression | Detect kmalloc and kvmalloc as allocator signals |
| Profile resources | Load 6 built-in profiles from a source checkout and smoke-test the default profile from an installed wheel |
| Verdict contract | Do not mistake not_cve_candidate for a positive finding |
| Follow-up policy | Allow two manual follow-ups and block a third request |
| Stale response handling | Archive and never reuse a response that has no pending target |
| Safe default | Keep the autopilot sandbox default at read-only |
| CI matrix | Run the regression suite on Python 3.11 and 3.12 |
python -m unittest discover -s tests -v
GitHub Actions runs unit regressions, installs the wheel into a fresh environment, and smoke-tests a default profile scan. The public case above is an operational outcome from real research, not a precision, recall, or CVE discovery-rate benchmark measured on a representative Linux tree corpus.
read-only sandbox.--dangerously-bypass-approvals-and-sandbox without an external sandbox.From the earliest version recorded in Git history, the goal was closer to controlling which code an LLM should inspect first and what evidence it must provide than to having the LLM find vulnerabilities on its own. The v1-assisted investigation that discovered CVE-2026-31720 demonstrated how a narrow investigation unit and evidence contract could be applied in real research. v2 extended this workflow into provenance-aware triage that preserves repository state and known references together. A new implementation would prioritize:
review and runner layers to eliminate CLI/autopilot duplication,The central principle worth retaining is still External Signal: do not ask an LLM to explore an entire codebase vaguely; use signals outside the model to narrow the investigation unit, then iterate around reachability and invariants.
Kernel Codex Harness does not replace Linux kernel vulnerability detection. Instead, it turns External Signal into an explainable ranking and constrains LLM review to a short, stateful investigation process. This structure was used in the real investigation that discovered CVE-2026-31720. The project's central result is not a claim of a new analysis algorithm, but the definition and practical application of LLM security review as a problem of external-signal attention allocation, evidence contracts, and reproducible orchestration.
.
├── .github/workflows/ci.yml
├── docs/
│ ├── assets/kernel-harness-architecture.svg
│ ├── AUTOPILOT.md
│ ├── CODEX_CLI.md
│ ├── CODEX_WORKFLOW.md
│ └── SYZBOT.md
├── kernel_harness/
│ ├── resources/
│ │ ├── linux-kernel-default.json
│ │ └── profiles/
│ ├── autopilot.py
│ ├── bundle.py
│ ├── cli.py
│ ├── ingest.py
│ ├── models.py
│ ├── prompting.py
│ ├── session.py
│ ├── syzbot.py
│ └── targeting.py
├── tests/test_regressions.py
├── README.md
└── pyproject.toml
Detailed operating procedures are available in docs/.
[1] Protect AI, “vulnhuntr,” GitHub repository. https://github.com/protectai/vulnhuntr
[2] Google, “syzkaller and syzbot,” GitHub repository. https://github.com/google/syzkaller
[3] OpenAI, “Codex CLI.” https://developers.openai.com/codex/cli/
Licensed under the Apache License 2.0.