SyzPilot is a state-guided agentic syscall specification synthesizer, designed for syzlang.
This document explains how to use SyzPilot to synthesize syscall specifications and run fuzzing for the Linux kernel. SyzPilot also supports FreeBSD, OpenBSD, NetBSD, and Android. The synthesized specs can also be used to fuzz gVisor and Starnix.
Note
The documentation is still being improved, and some content may contain typos. We are doing our best to review and fix them :)
Please replace the following variables according to your environment:
$SYZPILOT: directory for saving the SyzPilot source code.$KERNSRC: directory for saving the kernel source code.$IMAGE: directory for saving the vm image used for fuzzing.
We recommend running SyzPilot with Docker. You can build the Docker image with the following commands:
wget -O Dockerfile https://raw.githubusercontent.com/Radon10043/SyzPilot/main/docker/Dockerfile
docker build -t syzpilot:latest --network host -f ./Dockerfile .Start a container and enter it:
docker run \
-d \
-v ./vol:/vol \
--cpus 20 \
--network host \
--privileged \
--name syzpilot-test \
syzpilot:latest tail -f /dev/null
docker exec -it syzpilot-test bashWe recommend downloading fuzzers, kernels, images, and other artifacts to the mounted /vol directory for persistent storage :)
This section explains how to set up and use SyzPilot for syscall spec synthesis. All commands below are executed inside the container.
If you do not want to re-synthesize specs, you can reuse our synthesized specs, which are also used in our evaluation.
Download SyzPilot together with its syzkaller submodule:
export SYZPILOT=/vol/SyzPilot
git clone --recurse-submodules https://github.com/Radon10043/SyzPilot $SYZPILOT
# if you forgot to clone with --recurse-submodules, run `git submodule update --init --recursive` under $SYZPILOTPatch syzkaller to support additional constant extraction. SyzPilot validates synthesized specs with the syz-extract built from this syzkaller checkout, so apply the patches before building SyzPilot:
cd $SYZPILOT/syzkaller
git apply -3 ../patch/syzkaller/*
git reset .SyzPilot can be built with the following commands:
cd $SYZPILOT
makeSyzPilot needs to analyze the kernel and construct the corresponding knowledge base. Here, we use Linux v6.18 as an example:
export KERNSRC=/vol/linux/v6.18
git clone --depth 1 -b v6.18 https://github.com/torvalds/linux $KERNSRC
cp $SYZPILOT/configs/kernel/linux.config $KERNSRC/.config
cd $KERNSRC
make CC="ccache clang" olddefconfig modules_prepare all -j16
python3 scripts/clang-tools/gen_compile_commands.pyAnalyze the kernel compile commands and construct the knowledge base:
cd $SYZPILOT
./bin/analyzer -i $KERNSRC/compile_commands.json -o data/database/linux.db -j 16 > logs/analyze.log 2>&1Flags of analyzer:
-i: path tocompile_commands.json-o: path to the output database (default: ./data/kernel.db)-j: number of parallel jobs (default: 1)
Setup the .env file:
cd $SYZPILOT
echo "OPENAI_BASE_URL=[YOUR_BASE_URL]" > .env
echo "OPENAI_API_KEY=[YOUR_API_KEY]" >> .envPrepare a reference file for spec synthesis. Here, we use dvb_frontend_fops from the Linux DVB subsystem as an example:
cd $SYZPILOT
mkdir .workdir
echo "variable,dvb_frontend_fops" > .workdir/ref.txtManually enumerating all syscall related elements is tedious. You can use SyzPilot's minitask tool to automatically filter related elements and list the syscalls whose specs need to be synthesized.
Synthesize syscall specs:
cd $SYZPILOT
./bin/generator \
-db=./data/database/linux.db \
-outdir=./.workdir \
-kernel=$KERNSRC \
-os=linux \
-model=gemini-3-flash-preview \
-ref=./.workdir/ref.txt \
-jobs=4 > logs/generate.log 2>&1The synthesized specs are saved under ./.workdir/specs.
Caution
Watch the costs during synthesis!
Flags of generator:
- required:
-model: model to query, e.g. gemini-3-flash-preview-db: path to the kernel knowledge database produced in the Analyze kernel section-outdir: output path for specs synthesized by SyzPilot-kernel: path to the kernel used for spec validation-os: target OS type. Currently supported values are linux, freebsd, openbsd, netbsd, and android.-ref: path to the file containing reference global variables or functions for spec synthesis.
- optional:
-env: path to the .env file (default:$PWD/.env)-extract-bin: path tosyz-extract(default:$PWD/bin/syz-extract)-check-bin: path tosyz-check(default:$PWD/bin/syz-check)-sysdir: path to a directory such assyzkaller/sys. SyzPilot reuses specs under-sysdirto avoid duplicate synthesis of common flags, syscalls, and similar elements. (default:$PWD/syzkaller/sys)-resume: whether to resume previous progress (default: true)-max-fix: maximum number of attempts to fix a generated spec (default: 5)-max-retry: maximum number of outline-generate-fix attempts.-1means unlimited retries. (default: 5)-jobs: number of parallel jobs (default: 1)-otl-system-prompt: path to outline prompt file(s). Use commas to separate multiple files. (default:$PWD/data/prompts/outline/instruction.md,$PWD/data/prompts/outline/example_media.md,$PWD/data/prompts/outline/example_ppp.md)-gen-system-prompt: path to generation prompt file(s). Use commas to separate multiple files. (default:$PWD/data/prompts/generate/instruction.md,$PWD/data/prompts/generate/example_media.md,$PWD/data/prompts/generate/example_ppp.md)-fix-system-prompt: path to fix prompt file(s). Use commas to separate multiple files. (default:$PWD/data/prompts/fix/instruction.md,$PWD/data/prompts/fix/example_v4l2.md)
Refactor synthesized specs by adding a unique suffix to each element to avoid conflicts:
cd $SYZPILOT
./bin/refactor -indir=./.workdir/specs -outdir=./.workdir/refactored(Optional) Add meta arches["amd64"] to limit the scope of the specs:
sed -i '1i meta arches["amd64"]' .workdir/refactored/*.txtTODO: Currently, the variable or function name is added as a suffix to each spec element, e.g. ioctl$ABC -> ioctl$ABC_dvb_frontend_fops. However, this refactoring can be inconvenient for subsystem fuzzing since we have to list the full names of all synthesized syscalls to distinguish them from syzkaller's existing syscalls. We are considering a more suitable refactoring method.
Note
During integration, some errors in the synthesized specs may need to be fixed manually. This typically involves adjusting the order of include files and removing unused elements. SyzPilot provides several utility tools to help fix these errors.
Integrate the specs with syzkaller, extract constants, and build syzkaller:
cd $SYZPILOT/syzkaller
cp ../.workdir/refactored/* sys/linux
make bin/syz-extract
ls sys/linux/gen#*.txt | xargs -n 1 basename | xargs ./bin/syz-extract -build -sourcedir=$KERNSRC -os=linux -arch=amd64
make generate
make all -j16Create a Debian Bullseye image for fuzzing:
export IMAGE=/vol/images/Debian
mkdir -p $IMAGE && cd $IMAGE
cp $SYZPILOT/scripts/linux/create-image.sh .
chmod +x ./create-image.sh
./create-image.shStart fuzzing with the synthesized specifications:
cd $SYZPILOT
cat <<__EOF__ > .workdir/fuzz.cfg
{
"target": "linux/amd64",
"http": "127.0.0.1:56741",
".workdir": "$SYZPILOT/.workdir",
"kernel_obj": "$KERNSRC",
"image": "$IMAGE/bullseye.img",
"sshkey": "$IMAGE/bullseye.id_rsa",
"syzkaller": "$SYZPILOT/syzkaller",
"procs": 8,
"type": "qemu",
"reproduce": false,
"vm": {
"count": 4,
"kernel": "$KERNSRC/arch/x86/boot/bzImage",
"cpu": 8,
"mem": 2048
}
}
__EOF__
./syzkaller/bin/syz-manager -config=./.workdir/fuzz.cfgminitask selects global variables associated with syscalls by using string matching, then lists all syscalls that require specifications. It helps avoid the tedious process of manually enumerating syscall related elements and prevents duplicate spec synthesis for the same syscall. You can directly run generator based on the output of minitask for optimal efficiency.
Build:
make minitask # it will also be built via `make all`Set up the .env file if you have not already done so:
echo "OPENAI_BASE_URL=[YOUR_BASE_URL]" > .env
echo "OPENAI_API_KEY=[YOUR_API_KEY]" >> .envRun minitask to select all syscall related elements and enumerate all unique syscalls that require spec synthesis:
cd $SYZPILOT
./bin/minitask \
-db=./data/database/linux.db \
-os=linux \
-outdir=./.workdir/minitask \
-model=gemini-3-flash-preview > logs/minitask.log 2>&1Extract references:
./scripts/reflist.sh ./.workdir/minitask > ./.workdir/minitask/ref.txtThen run generator with -outdir=./.workdir/minitask -ref=./.workdir/minitask/ref.txt to reuse the results of minitask.
Remove unused elements in place:
$SYZPILOT/bin/rmunused -indir=$SYZPILOT/syzkaller/sys/linuxSpecifications used in our evaluation are saved under $SYZPILOT/patch/specs-*. Feel free to reuse them to avoid duplicate synthesis. Remember to apply the syzkaller patches first (see Download SyzPilot); they also enable syzkaller to fuzz the OpenBSD kernel on Linux.
specs-kern stores specs for full kernel fuzzing:
cd $SYZPILOT/syzkaller
git apply ../patch/specs-kern/*specs-subsys stores specs for subsystem fuzzing. We re-synthesize specs for subsystems that already have specs:
cd $SYZPILOT/syzkaller
git apply ../patch/specs-subsys/*specs-ablation stores specs synthesized by the variants used in our ablation study.
Please follow setup-env.md to set up the evaluation environment. You can then reproduce our evaluation using the Docker Compose files.
Please see subsystem.md for instructions on re-synthesizing specs for a subsystem that already has specs.
Note
We will update the trophies as soon as new specs synthesized by SyzPilot are merged into the syzkaller repository or related issues are assigned CVEs.
- sys/linux: add descriptions for mtd and tee subsystems
- sys/netbsd: add and update descriptions for acpi, agp, hdaudio, etc.
- sys/freebsd: generate headers for const extraction and add syscall descriptions
- sys/openbsd: update wscons.txt and add dev_dri.txt
- sys/freebsd: add descriptions for acpi, apm, and auditpipe devices
- sys/linux: update syscall descriptions for multiple file systems
- Add descriptions for XFS subsystem
- sys/linux: add descriptions for dvb subsystem
- sys/linux: update flags in dev_video4linux.txt
- sys/linux: add v4l2_meta_format
- CVE-2026-23214, WARNING in find_free_extent
- CVE-2026-23249, general protection fault in xchk_btree
- CVE-2026-23250, general protection fault in xchk_metadata_inode_forks
- CVE-2026-23251, general protection fault in xfarray_destroy
- CVE-2026-23252, general protection fault in alloc_file_pseudo
- CVE-2026-23223, KASAN slab-use-after-free Read in xchk_btree_check_block_owner
- KASAN: slab-use-after-free Write in dvb_device_open
- KASAN: slab-use-after-free Read in dvb_frontend_thread
- KFENCE: use-after-free read in dvb_frontend_release
- WARNING: still has locks held in _dmxdev_lock
- WARNING: bad unlock balance in _dmxdev_unlock
- WARNING in iterate_dir
- WARNING in exc_debug_kernel
- general protection fault in dvb_device_open
- possible deadlock in dvb_dvr_release
- possible deadlock in ocfs2_try_to_free_truncate_log (syzbot report before but cannot generate reproducer)
Bugs related to our generated specifications and reported by syzbot:
- CVE-2026-23253, BUG: corrupted list in io_poll_remove_entries
- CVE-2026-64359, INFO: task hung in nilfs_transaction_begin
- CVE-2026-31577, general protection fault in nilfs_mdt_save_to_shadow_map
- CVE-2026-31585, memory leak in vidtv_psi_service_desc_init
- CVE-2026-43058, KMSAN: uninit-value in vidtv_ts_null_write_into
- CVE-2026-53320, WARNING in nilfs_btree_mark
- CVE-2026-53320, WARNING in nilfs_ioctl_prepare_clean_segments
- BUG: corrupted list in nilfs_lookup_dirty_data_buffers
- INFO: trying to register non-static key in as102_dvb_dmx_start_feed
- KASAN: slab-use-after-free Read in dvb_device_open
- KASAN: slab-use-after-free Read in dvb_frontend_release
- KASAN: slab-use-after-free Read in dvb_frontend_open
- KMSAN: uninit-value in dvbdmx_release_ts_feed
- KMSAN: uninit-value in dvb_demux_read
- WARNING in as102_dvb_dmx_start_feed media
- general protection fault in bio_add_page
- general protection fault in bio_alloc_bioset
- memory leak in dvb_register_device
- memory leak in vidtv_psi_short_event_desc_init
- Fatal trap NUM: general protection fault while in kernel mode in cam_periph_runccb
- Fatal trap NUM: page fault while in kernel mode in cam_periph_runccb
- Fatal trap NUM: page fault while in kernel mode in passdoioctl
- Fatal trap NUM: page fault while in kernel mode in passsendccb
- panic: _free(NUM): address ADDR(ADDR) has not been allocated
- panic: ata_action: ccb ADDR, func_code CODE should not be allocated from UMA zone
- panic: AUX register unsupported
- panic: cam_periph_ccbwait: proceeding with incomplete ccb
- panic: dst_m ADDR is not wired
- panic: mutex ACPI global lock owned at ../../../kern/kern_event.c:LINE
Test cases generated by SyzPilot have been incorporated into FreeBSD's test suite:
- assert failed: chp->ch_drive[drive].drv_softc == NULL
- assert failed: hispgrp->pg_jobc > NUM
- assert failed: it->it_time.it_value.tv_sec >= NUM
- assert failed: kn->kn_fop == &proc_filtops
- assert failed: kq->kq_fdp == fdp
- assert failed: ks->ks_pshared_proc == NULL
- assert failed: ps->ps_endoffset != endoffset
- assert failed: sc->sc_base.me_evp != NULL
- assert failed: ts->tv_nsec >= NUM
- assert failed: uio->uio_iovcnt > NUM
- panic: ASan: Unauthorized Access In ADDR: Addr ADDR [ADDR bytes, read, KmemRedZone]
- panic: ASan: Unauthorized Access In ADDR: Addr ADDR [NUM byte, read, KmemRedZone]
- panic: LOCKDEBUG: Mutex error: rw_vector_enter,NUM: spin lock held
If you find SyzPilot helpful, please cite it. Thanks.
@inproceedings{Zhang2027SyzPilot,
title = {{SyzPilot}: State-Guided Agentic Syscall Specification Synthesis for Enhancing Kernel Fuzzing},
author = {Zhang, Jiaming and Sun, Chang-ai and Liu, Huai and Cui, Zhanqi},
booktitle = {Proceedings of the Network and Distributed System Security Symposium},
year = {2027},
note = {{Just Accepted}}
}Intermediate data, including LLM query records and fuzzing results (~30 GB): Google Drive.
Thanks Shifan Liu (USTB), Huiwen Yang (NUAA), and Xiaoyang Han (NJU) for their support and suggestions throughout the work.