This guide documents the expected environment, configuration, command order, and output locations for PPatch Partner.
PPatch Partner assumes a repeatable experiment unit:
- A patch file:
patches/<poc_id>.patch - A POC directory:
pocs/<poc_id>/ - A Linux source tree initialized at a configured tag
- A base kernel config
- A VM image and SSH keys
The typical flow is:
- Clone and initialize the Linux source tree.
- Apply patches and create one git tag per successful patch.
- Prepare a per-POC overlay directory.
- Extract patch-required config options and build the kernel.
- Retry failed builds with compiler diagnostics when useful.
- Start QEMU, run POCs, and scan logs for crash signatures.
- Optionally run syzkaller reproduction or corpus replay jobs.
Settings are read from, in order:
- Environment variables.
PPATCH_PARTNER_ENV_FILE, if set.src/ppatch_partner/config/config.env, if it exists.src/ppatch_partner/config/config.example.envas a safe fallback.
Create a private local config:
cp src/ppatch_partner/config/config.example.env src/ppatch_partner/config/config.envKeep config.env out of git. It may contain private paths, database URLs, image locations, or SSH key paths.
WORKSPACE_ROOT is the base directory for relative workspace paths. For example:
WORKSPACE_ROOT=.
LINUX_SOURCE=./linux_source
PATCH_DIR=./patches
WORK_DIR_PATH=./expsLINUX_DIR is computed as:
<LINUX_SOURCE>/<LTS_VERSION>/<LINUX_TAG>/<LINUX_ARCH>/linux
With the example values, that becomes:
./linux_source/longterm/v6.9.5/x86_64/linux
Create or mount these directories before running full workflows:
.
├── patches/
│ ├── poc_0.patch
│ └── poc_1.patch
├── pocs/
│ ├── poc_0/
│ │ ├── poc
│ │ └── poc.syz
│ └── poc_1/
├── configs/
│ └── x86_64_common_config
├── images/
│ ├── bullseye.img
│ ├── bullseye.id_rsa
│ └── bullseye.id_ed25519
├── linux_source/
├── exps/
└── ppatch_log/
The current VM helper scripts expect a Bullseye image naming convention. If your files are named differently, update src/scripts/startvm, connectvm, scptovm, and related scripts.
Install system dependencies:
sudo apt-get update
sudo apt-get install -y \
build-essential gcc g++ \
libncurses-dev bison flex libssl-dev libelf-dev \
git curl wget patch sudo psmisc openssh-client cpio bc \
qemu-system-x86 qemu-utilsInstall Python dependencies:
pdm use -f 3.12
pdm installSet git identity. The patch workflows create commits and tags inside the Linux source tree:
git config --global user.name "Your Name"
git config --global user.email "you@example.com"Build the image and show CLI help:
docker compose up --build ppatch-partnerStart a shell:
docker compose run --rm ppatch-partner bashRun a module:
docker compose run --rm ppatch-partner \
python -m ppatch_partner.scripts.ppatch_auto main --use-gnu-firstCompose starts a local PostgreSQL service. The default credentials are for local development only:
postgresql://ppatch:ppatch@postgres:5432/ppatch_partner
Override them for any shared or long-lived deployment.
Default initialization:
pdm run python -m ppatch_partner.scripts.initialize mainOverride key options:
pdm run python -m ppatch_partner.scripts.initialize main \
--output-dir ./linux_source \
--lts-version longterm \
--mirrors-linux-source https://git.kernel.org/pub/scm/linux/kernel/git/stable/ \
--linux-tag v6.9.5 \
--linux-arch x86_64This command:
- Creates standard workspace directories.
- Clones
<MIRRORS_LINUX_SOURCE><TREE_VERSION>.git. - Checks out
LINUX_TAG. - Adjusts
kernel/module/Kconfig. - Creates the base tag from
LINUX_PATCH_BASE_TAG.
Patch only Kconfig and recreate the base tag:
pdm run python -m ppatch_partner.scripts.initialize fix-kconfig \
--repo-path ./linux_source/longterm/v6.9.5/x86_64/linuxTry GNU patch first, then fall back to ppatch auto:
pdm run python -m ppatch_partner.scripts.ppatch_auto main --use-gnu-firstUse only ppatch auto:
pdm run python -m ppatch_partner.scripts.ppatch_auto mainOutputs:
- Generated auto patches:
ppatch_log/<LTS_VERSION>/<LINUX_TAG>/ - Failure list:
ppatch_log/<LTS_VERSION>/<LINUX_TAG>/failure_patches.txt - Linux git commits and tags for successful patches.
Apply multiple patches from a list:
pdm run python -m ppatch_partner.scripts.ppatch_auto apply-multiple \
--poc-list-file ./multiple.txtApply multiple patches using conflict-aware ordering:
pdm run python -m ppatch_partner.scripts.ppatch_auto apply-multiple-optimized \
--poc-list-file ./multiple.txt \
--max-patches 20Run the complete per-POC overlay workflow:
pdm run python -m ppatch_partner.scripts.overlay auto --cve-id poc_0Run individual steps:
pdm run python -m ppatch_partner.scripts.overlay init --cve-id poc_0
pdm run python -m ppatch_partner.scripts.overlay mount --cve-id poc_0
pdm run python -m ppatch_partner.scripts.overlay compile-kernel --cve-id poc_0
pdm run python -m ppatch_partner.scripts.overlay unmount --cve-id poc_0Important output locations:
exps/<poc_id>/<LTS_VERSION>/<LINUX_ARCH>/
exps/compilation_logs/<LTS_VERSION>/<LINUX_TAG>/
The build currently checks for:
upper_dir/arch/x86/boot/bzImage
When a build fails, stderr may contain JSON diagnostics. Retry patching from that data:
pdm run python -m ppatch_partner.scripts.auto_ppatch_retry main \
--cve-id poc_0 \
--max-retries 3The retry workflow:
- Reads
stderr_<poc_id>_attempt<N>.log. - Extracts JSON diagnostics next to the log.
- Runs
ppatch autowith the diagnostics file. - Creates retry tags such as
poc_0_retry2. - Rebuilds until success or retry exhaustion.
Run one POC in QEMU and scan VM logs:
pdm run python -m ppatch_partner.scripts.monitor_crash main --cve-id poc_0Run a list:
pdm run python -m ppatch_partner.scripts.monitor_crash main \
--cve-list ./poc_list.txt \
--concurrency 2Results are written to:
panic_list.txt
no_panic_list.txt
Run one syzkaller reproduction:
pdm run python -m ppatch_partner.scripts.reproduce single poc_0Run a batch:
pdm run python -m ppatch_partner.scripts.reproduce batch ./poc_list.txt --run-time 24Inspect or stop running instances:
pdm run python -m ppatch_partner.scripts.reproduce status
pdm run python -m ppatch_partner.scripts.reproduce stop poc_0
pdm run python -m ppatch_partner.scripts.reproduce stopExtract corpus databases:
pdm run python -m ppatch_partner.scripts.reproduce extract-corpus-cmd \
./poc_list.txt v6.9.5 ./corpus_exportsLoad corpus databases and run corpus replay:
pdm run python -m ppatch_partner.scripts.reproduce load-corpus-cmd \
./poc_list.txt v6.9.5 ./corpus_exports --run-time 12Clean experiment directories, tags, and temporary files:
pdm run python -m ppatch_partner.scripts.clean main --clean-type allClean only Linux worktree state:
pdm run python -m ppatch_partner.scripts.clean main --clean-type gitClean only tags created by workflows:
pdm run python -m ppatch_partner.scripts.clean main --clean-type tagsInstall dependencies through PDM:
pdm installThe project requires Python 3.12:
pdm use -f 3.12Check:
- The command is running on Linux.
- The user can run
sudo mount. - No stale mount is still attached.
- The kernel source directory exists.
Check:
qemu-system-x86_64is installed.- KVM is available, or update VM scripts for non-KVM use.
images/contains the expected Bullseye image and SSH keys.startvmis executable in the experiment directory.
The patch workflows skip existing tags. Remove or rename old tags if you need to reprocess a patch:
git -C ./linux_source/longterm/v6.9.5/x86_64/linux tag -d poc_0Database persistence is optional. If you are not saving to the database, avoid --save-to-db. If you are using Docker Compose, wait for PostgreSQL health checks to pass.