
Passive Linux host vulnerability scanner for CVE-2026-31694: checks running kernel, FUSE config, package metadata, and patch evidence, then generates tailored mitigation guidance.
A passive Linux host assessment tool for CVE-2026-31694, a FUSE readdir-cache out-of-bounds write in the Linux kernel.
The scanner evaluates the running kernel, FUSE exposure, local package metadata, kernel source evidence, execution environment, and operating-system family. When the host may be affected, it produces a mitigation guide tailored to the detected distribution.
Author: Aung Myat Thu [w01f]
cve_2026_31694_scanner.py is a local, non-exploit vulnerability assessment utility.
It does not confirm the vulnerability by triggering the affected FUSE code path. Instead, it combines multiple passive indicators:
CONFIG_FUSE_FS kernel configuration;/dev/fuse presence and current-user access;The result is an exposure assessment, not an exploit-based proof.
CVE-2026-31694 affects the Linux kernel FUSE readdir cache.
The vulnerable logic calculates the serialized size of a directory entry from a server-controlled name length and copies the record into a single page-cache page. A directory entry with a name length of 4095 can produce a serialized size of 4120 bytes.
On a system using 4096-byte pages, that record exceeds one page by 24 bytes.
The upstream correction rejects directory records that cannot fit inside a single page before the cache copy occurs.
fs/fuse/readdir.c
fuse_add_dirent_to_cache()
The scanner contains the following upstream stable fixed baselines:
These versions are used as one assessment input. They are not treated as the only source of truth because distribution vendors may backport the correction while retaining an older upstream version number.
The scanner is designed to avoid invoking the vulnerable operation.
/dev/fuse;/etc/passwd;/dev/fuse permissions;/proc, /boot, /lib/modules, /usr/lib/modules, /usr/src, and package metadata directories;--output is explicitly supplied;Mitigation commands are documentation output. They are never executed automatically.
platform.release() and parses the upstream-style kernel version./etc/os-release or /usr/lib/os-release.CONFIG_FUSE_FS from available kernel configuration sources./proc/filesystems./dev/fuse metadata and current-user read/write access.fusermount3 or fusermount./proc/self/mountinfo.CVE-2026-31694;/var/run/reboot-required where applicable.systemd-detect-virt when available.The scanner uses only the Python standard library.
Additional evidence is collected when the following tools are present:
Root privileges are not required for the normal scan.
Some evidence may be unavailable to an unprivileged account because of system-specific file permissions.
Place the scanner in a working directory:
chmod +x cve_2026_31694_scanner.py
Run it directly:
./cve_2026_31694_scanner.py
Or invoke it with Python:
python3 cve_2026_31694_scanner.py
No Python package installation is required.
python3 cve_2026_31694_scanner.py
python3 cve_2026_31694_scanner.py --format json
The compatibility alias below produces the same format:
python3 cve_2026_31694_scanner.py --json
python3 cve_2026_31694_scanner.py --format markdown
python3 cve_2026_31694_scanner.py \
--format markdown \
--output cve-2026-31694-report.md
python3 cve_2026_31694_scanner.py --mitigation-only
python3 cve_2026_31694_scanner.py --no-package-query
python3 cve_2026_31694_scanner.py --no-exit-status
This option is useful when collecting reports without allowing vulnerability status to fail an orchestration job.
The scanner evaluates evidence in the following order.
The scanner determines:
Environment detection changes the interpretation of package and reboot data.
For example, a container can see the host kernel but normally cannot determine the host kernel package from the container filesystem.
The scanner searches for a running-kernel configuration in:
/proc/config.gz
/boot/config-$(uname -r)
/lib/modules/$(uname -r)/build/.config
/usr/lib/modules/$(uname -r)/build/.config
It evaluates:
CONFIG_FUSE_FS=y
CONFIG_FUSE_FS=m
CONFIG_FUSE_FS is not set
Interpretation:
| Value | Meaning |
|---|---|
y | FUSE is built into the kernel. Module blacklisting cannot disable it. |
m | FUSE is available as a loadable module. |
The scanner checks:
/proc/filesystems
/dev/fuse
PATH for fusermount3 or fusermount
/proc/self/mountinfo
The absence of /dev/fuse or fusermount3 reduces immediate usability from the current environment, but does not prove that the kernel is patched.
The documented oversized record is 4120 bytes.
When the host page size is at least 4120 bytes, the documented record does not cross a page boundary. The scanner may report that the system is not exposed by the documented page geometry.
This conclusion applies only to the documented record and assessed implementation behavior. It is not a general statement that all FUSE memory-safety issues are impossible on larger-page systems.
When installed source is available, the scanner searches:
fs/fuse/readdir.c
It looks for the affected function and a guard equivalent to rejecting reclen > PAGE_SIZE.
Direct source evidence has higher confidence than version comparison.
The inspected source must correspond to the running kernel for the result to be authoritative. Distribution source trees under /usr/src may represent a different installed kernel.
The scanner attempts to map the running kernel image to its owning package using the detected distribution's package database.
Possible mechanisms include:
dpkg-query -S
rpm -qf
pacman -Qo
apk info -W
When a package is identified, local changelog metadata may be inspected for the CVE ID or patch title.
No changelog match is not proof of vulnerability. Vendors do not always include individual CVE references in locally installed changelogs.
The parsed kernel version is compared with the scanner's fixed baseline table.
Version comparison produces a heuristic result because:
Where supported, the scanner runs read-only queries against existing local metadata.
It does not refresh repositories.
Examples:
apt list --upgradable
pacman -Qu
dnf --cacheonly --quiet check-upgrade
zypper --no-refresh list-patches --all --cve=CVE-2026-31694
apk version -l <
A result showing no available update is only as current as the cached repository metadata.
On a normal host or virtual machine, the scanner compares the running kernel with detected installed kernels and checks the reboot marker used by Debian-family systems.
Inside a container or WSL guest, this check is marked as host-controlled instead of comparing guest files with the host kernel.
Evidence precedence is approximately:
PATCHEDDirect local evidence indicates that the correction exists.
Examples:
Confidence is normally high, but source-to-running-kernel alignment should still be verified.
NOT_EXPOSEDThe detected running-kernel configuration has FUSE disabled.
This is an exposure result, not evidence that the kernel source itself contains the correction.
NOT_EXPOSED_BY_DOCUMENTED_PAGE_GEOMETRYThe memory page size is large enough to contain the documented 4120-byte record without crossing a page boundary.
This verdict is limited to the documented vulnerability geometry.
UPSTREAM_BASELINE_SAFEThe parsed running version meets a known fixed upstream baseline or predates the affected implementation.
Vendor package verification remains preferable.
POTENTIALLY_VULNERABLEThe parsed kernel is within an affected upstream range and no direct local patch evidence was found.
This result requires checking the operating-system vendor advisory before declaring the installed kernel vulnerable.
INCONCLUSIVEThe scanner could not obtain enough evidence.
Common causes:
NOT_APPLICABLEThe current operating system is not Linux.
| Code | Meaning |
|---|---|
0 | Patched evidence, safe upstream baseline, or documented exposure absent |
Example:
python3 cve_2026_31694_scanner.py
status=$?
case "$status" in
0)
echo "No actionable exposure detected by the scanner"
;;
1)
echo "Potential exposure detected"
;;
2)
echo "Assessment inconclusive"
;;
3)
echo "Scanner execution failed"
;;
esac
When --no-exit-status is specified, the scanner returns 0 after producing the report.
The scanner uses the following local sources where available:
The absence of a locally visible update does not mean a fixed package is unavailable. Repository metadata may be stale or the distribution may not yet have published a package.
Mitigation guidance is selected according to:
/dev/fuse availability;The scanner prints commands but never executes them.
Typical generated sequence:
sudo apt update
sudo apt full-upgrade
sudo reboot
Review the proposed package transaction before approving it.
After reboot:
uname -r
python3 cve_2026_31694_scanner.py
Typical generated sequence:
sudo pacman -Syu
sudo reboot
Arch-family systems require a complete system upgrade. Do not refresh package databases with pacman -Sy and then install selected packages without completing the upgrade.
Typical generated sequence:
sudo dnf upgrade --refresh
sudo systemctl reboot
Systems using yum may receive the corresponding yum command.
Typical generated sequence:
sudo zypper refresh
sudo zypper patch --cve=CVE-2026-31694
sudo zypper patch --category security
sudo systemctl reboot
The CVE-specific patch may not be available as a separate transaction on every SUSE product or repository state.
Typical generated sequence:
doas apk update
doas apk upgrade
doas reboot
The privilege prefix depends on whether the scanner finds sudo, doas, or an already-root session.
Typical generated sequence:
sudo emaint sync -a
sudo emerge --ask --update --deep --newuse @world
Kernel installation and activation depend on the host's configured Gentoo kernel workflow.
Typical generated sequence:
sudo xbps-install -Suv
sudo reboot
Typical generated sequence:
sudo nixos-rebuild switch --upgrade
sudo systemctl reboot
When patching cannot be completed immediately, the scanner may suggest temporary controls.
Possible recommendations include:
/dev/fuse;/dev/fuse from untrusted containers;CAP_SYS_ADMIN;--privileged containers.These controls can disrupt legitimate workloads, including:
Temporary restrictions must not replace installation of a vendor-fixed kernel.
Containers normally share the host kernel.
A scanner executed inside a container may detect:
/dev/fuse, even when the host has FUSE enabled.For this reason, the scanner marks host-dependent evidence as non-authoritative and directs mitigation to the container host.
Run the scanner directly on the host for an authoritative package and reboot assessment.
Container hardening guidance includes removing:
--device /dev/fuse
--cap-add SYS_ADMIN
--privileged
Equivalent Compose configuration should also be reviewed:
devices:
- /dev/fuse:/dev/fuse
cap_add:
- SYS_ADMIN
privileged: true
The WSL kernel is managed by Windows rather than the Linux guest distribution's kernel package.
Typical generated guidance:
wsl --update
wsl --shutdown
Run these commands from Windows PowerShell, restart the distribution, and rerun the scanner.
Updating only guest packages with apt, dnf, or another Linux package manager does not replace the WSL kernel.
A virtual machine normally owns its guest kernel. Distribution-specific package and reboot checks remain applicable inside the guest.
The hypervisor host should be assessed separately.
Text output is intended for direct terminal use.
python3 cve_2026_31694_scanner.py --format text
The report contains:
JSON output contains the complete dataclass-based report.
python3 cve_2026_31694_scanner.py \
--format json \
--output report.json
Example field selection:
python3 cve_2026_31694_scanner.py --format json --no-exit-status |
python3 -c '
import json
import sys
report = json.load(sys.stdin)
print(report["hostname"])
print(report["verdict"])
print(report["confidence"])
print(report["kernel_release"])
'
Markdown output is suitable for technical records, tickets, and assessment reports.
python3 cve_2026_31694_scanner.py \
--format markdown \
--output report.md
python3 cve_2026_31694_scanner.py \
--mitigation-only \
--format markdown
With JSON format, mitigation-only mode returns the mitigation object rather than the complete host report.
#!/usr/bin/env bash
set -u
report="cve-2026-31694-$(hostname)-$(date +%Y%m%d-%H%M%S).json"
python3 cve_2026_31694_scanner.py \
--format json \
--output "$report"
status=$?
case "$status" in
0)
echo "Assessment completed without a potential-vulnerability verdict"
;;
1)
echo "Potential CVE-2026-31694 exposure detected: $report" >&2
;;
2)
echo "Assessment inconclusive: $report" >&2
;;
*)
echo "Scanner failure: $report" >&2
;;
esac
exit "$status"
python3 cve_2026_31694_scanner.py \
--format json \
--output /var/tmp/cve-2026-31694.json \
--no-exit-status
30 3 * * * /usr/bin/python3 /opt/security/cve_2026_31694_scanner.py --format json --output /var/log/cve-2026-31694.json --no-exit-status
This overwrites the same report file. Use a wrapper script when historical reports are required.
[Unit]
Description=Passive CVE-2026-31694 assessment
After=local-fs.target
[Service]
Type=oneshot
ExecStart=/usr/bin/python3 /opt/security/cve_2026_31694_scanner.py --format json --output /var/log/cve-2026-31694.json --no-exit-status
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/log
The scanner needs read access to kernel and package metadata. Additional sandbox restrictions may hide evidence and produce an inconclusive report.
For fleet assessment:
Do not treat a scan from a container image build as a host-kernel assessment.
POTENTIALLY_VULNERABLEIdentify the exact running kernel:
uname -r
Identify the installed distribution and release:
cat /etc/os-release
Review the distribution vendor's CVE status for the exact kernel package and flavor.
Refresh package metadata.
install the vendor-supported fixed kernel package.
Reboot the host.
Confirm the running kernel changed:
uname -r
Rerun the scanner.
INCONCLUSIVECollect missing evidence:
uname -a
getconf PAGE_SIZE
cat /etc/os-release
grep CONFIG_FUSE_FS /boot/config-"$(uname -r)" 2>/dev/null
cat /proc/filesystems
ls -l /dev/fuse 2>/dev/null
findmnt -t fuse,fuseblk,fusectl
Then verify the exact kernel package against the vendor advisory.
Installing a fixed kernel package does not change the currently executing kernel.
Verify both installed and running kernels, then reboot as required.
Examples:
uname -r
ls -1 /lib/modules
Debian-family systems may also provide:
test -e /var/run/reboot-required && cat /var/run/reboot-required
Before temporarily unloading or blocking FUSE:
findmnt -t fuse,fuseblk,fusectl
lsmod | grep '^fuse'
ps aux | grep -E '[f]usermount|[s]shfs|[r]clone'
Do not unload the module while active FUSE filesystems are mounted.
The scanner intentionally does not trigger the vulnerable code path.
A POTENTIALLY_VULNERABLE verdict means the available passive evidence is consistent with exposure. It is not proof that exploitation will succeed.
Distribution kernels frequently backport security corrections.
An older-looking upstream version may be patched.
The vendor advisory and exact package release are authoritative for distribution-managed kernels.
A source tree found under /usr/src may not match the running kernel.
Source patch evidence should be validated against:
uname -r
and the source package or build directory used for that exact kernel.
The scanner does not refresh repositories.
Package update checks depend on existing local cache state.
Use the generated distribution-specific refresh and upgrade commands for an up-to-date package decision.
A container sees the host kernel but normally has different package metadata.
Run the scanner on the host.
Linux guest package state does not determine WSL kernel patch status.
Update WSL from Windows.
Custom, embedded, appliance, vendor, and self-built kernels may not have package metadata or recognizable version strings.
Inspect the actual source commit or vendor release notes.
The scanner does not determine whether a live-patch framework has applied this specific correction to the running kernel.
Use the live-patch vendor's status tools and advisory data.
The scanner does not verify:
The scanner assesses the current process namespace.
Mounts or device access hidden by namespaces may not appear in the report.
Kernel configuration could not be determinedCheck whether one of these files exists:
ls -l /proc/config.gz
ls -l /boot/config-"$(uname -r)"
ls -l /lib/modules/"$(uname -r)"/build/.config
Some distributions do not expose the running configuration through /proc/config.gz.
Running kernel package could not be identifiedPossible causes:
Check:
uname -r
ls -l /boot
ls -l /lib/modules/"$(uname -r)"
Refresh package metadata using the generated mitigation guide, then rerun the scanner.
The passive scanner does not refresh repositories automatically.
POTENTIALLY_VULNERABLE after installing updatesConfirm that the new kernel is running:
uname -r
Compare it with installed kernel directories:
ls -1 /lib/modules
A reboot may still be required.
Also check the vendor advisory because a fixed backport may not match the upstream version baseline.
When the report shows:
CONFIG_FUSE_FS=y
modprobe -r fuse and module blacklisting cannot disable FUSE.
Use a patched kernel or a kernel built without FUSE support.
The scanner may need read access to:
/proc
/boot
/lib/modules
/usr/lib/modules
/usr/src
package database directories
package cache directories
Adjust service hardening so these paths remain readable.
Recommended project layout:
cve-2026-31694-scanner/
├── README.md
└── cve_2026_31694_scanner.py
Recommended installed layout:
/opt/security/cve-2026-31694/
├── README.md
└── cve_2026_31694_scanner.py
Example:
sudo install -d -m 0755 /opt/security/cve-2026-31694
sudo install -m 0755 \
cve_2026_31694_scanner.py \
/opt/security/cve-2026-31694/
sudo install -m 0644 \
README.md \
/opt/security/cve-2026-31694/
Aung Myat Thu [w01f]
| Kernel series | Fixed baseline |
|---|
| 5.10 | 5.10.258 |
| 5.15 | 5.15.209 |
| 6.1 | 6.1.175 |
| 6.6 | 6.6.136 |
| 6.12 | 6.12.84 |
| 6.18 | 6.18.25 |
| 7.0 | 7.0.2 |
| Mainline | 7.1 or newer |
| Tool | Purpose |
|---|
dpkg-query | Debian/Ubuntu kernel package ownership and changelog discovery |
rpm | Fedora/RHEL/SUSE package ownership and changelog inspection |
pacman | Arch-family kernel package and update metadata |
apt | Debian-family cached update query |
dnf or dnf5 | Fedora/RHEL cached update query |
zypper | SUSE cached CVE patch query |
apk | Alpine package/update query |
systemd-detect-virt | Container and virtual-machine detection |
| Option | Description |
|---|
--format text | Human-readable terminal output. This is the default. |
--format json | Complete structured report as JSON. |
--format markdown | Technical Markdown report. |
--json | Alias for --format json. |
--output PATH | Write the report to a file instead of standard output. |
--mitigation-only | Return only the generated mitigation section. |
--no-package-query | Skip local package-manager update queries. |
--no-exit-status | Always exit with status 0 after generating output. |
-h, --help | Display command help. |
n |
| FUSE is disabled in the detected configuration. |
| unknown | No readable configuration source was available. |
1| Potentially vulnerable |
2 | Inconclusive |
3 | Scanner or report-generation error |
| Source | Information |
|---|
/etc/os-release | Distribution identity |
/usr/lib/os-release | Distribution identity fallback |
/proc/version | Kernel and WSL indicators |
/proc/config.gz | Running-kernel configuration |
/boot/config-* | Kernel configuration |
/lib/modules/*/build/.config | Kernel build configuration |
/proc/filesystems | Registered filesystems |
/proc/self/mountinfo | Active FUSE mounts |
/proc/1/cgroup | Container indicators |
/dev/fuse | Device presence and permissions |
/usr/src/*/fs/fuse/readdir.c | Optional source-level patch evidence |
/lib/modules | Installed kernel inventory |
/usr/lib/modules | Installed kernel inventory |
/boot/vmlinuz-* | Installed kernel inventory |
/var/run/reboot-required | Debian-family reboot indicator |
| package database | Kernel package ownership |
| package changelogs | Optional local backport evidence |
| package cache | Optional update availability evidence |
| Family | Recognized distributions |
|---|
| Ubuntu | Ubuntu, Linux Mint, Pop!_OS, elementary OS, Zorin OS |
| Debian | Debian, Kali Linux, Raspbian, Parrot OS |
| Arch | Arch Linux, Manjaro, Garuda Linux, EndeavourOS, CachyOS |
| Fedora | Fedora Linux |
| RHEL | RHEL, CentOS, Rocky Linux, AlmaLinux, Oracle Linux, Amazon Linux |
| SUSE | openSUSE Tumbleweed, openSUSE Leap, SLES, SLED |
| Alpine | Alpine Linux |
| Gentoo | Gentoo Linux |
| Void | Void Linux |
| NixOS | NixOS |
| Unknown | Generic Linux guidance |
| Family | Read-only update query |
|---|
| Debian/Ubuntu | apt list --upgradable |
| Arch | pacman -Qu |
| Fedora/RHEL | cached dnf check-upgrade |
| SUSE | cached CVE patch listing with zypper |
| Alpine | apk version -l < |
| Gentoo | Not implemented |
| Void | Not implemented |
| NixOS | Not implemented |
| Unknown | Not implemented |