Disable vsyscall emulate execution only
Requires the kernel to be built without CONFIG_LEGACY_VSYSCALL_XONLY=y, favoring full removal via NONE instead of an execute-only vsyscall page.
Checked against the resolved running state (e.g. sshd -T, sysctl, systemctl show), catches drop-ins and Includes a file read would miss. Caveat: runtime ≠ persistence; a value correct now may not survive a reboot.
Pavois asserts the effective configuration, the live, resolved state, not a file. File-based scanners (OVAL/SCAP, Lynis) miss Includes, drop-ins and runtime defaults; this check sees what is actually applied.
A mapping is a cross-reference to where each standard places this requirement, anchored and cross-validated, not a claim of equivalence. A passing check is evidence toward these references, how to read it.
Why this rule matters
CONFIG_LEGACY_VSYSCALL_XONLY=y keeps the vsyscall page mapped execute-only at its fixed address: reads are blocked but the page is still present and executable. Because its address and contents are fixed, the executable bytes can still be used as ROP gadgets to bypass ASLR. XONLY is a hardening improvement over EMULATE but weaker than removing the page; the rule prefers NONE (no mapping at all), so it flags a kernel that only restricts vsyscall to execute-only.
What Pavois checks
Pavois reads the build configuration of the running kernel, /boot/config-$(uname -r) and the in-memory /proc/config.gz, and confirms CONFIG_LEGACY_VSYSCALL_XONLY is not set to y. vsyscall mode is a compile-time choice, so reading the booted kernel's real config (not a stale /boot/config-*) shows which variant is actually active.
describe command("C=/boot/config-$(uname -r); if [ -r \"$C\" ]; then cat \"$C\"; elif zcat /proc/config.gz 2>/dev/null | head -1 | grep -q .; then zcat /proc/config.gz; else echo PAVOIS_NO_KERNEL_CONFIG; fi | grep -E '^(CONFIG_LEGACY_VSYSCALL_XONLY=|PAVOIS_NO_KERNEL_CONFIG)'") do
its('stdout') { should_not match(/PAVOIS_NO_KERNEL_CONFIG/) }
its('stdout') { should_not match(/^CONFIG_LEGACY_VSYSCALL_XONLY=y$/) }
endHow to verify it is applied
Run grep '^CONFIG_LEGACY_VSYSCALL' /boot/config-$(uname -r). Expected: CONFIG_LEGACY_VSYSCALL_XONLY absent or is not set, with CONFIG_LEGACY_VSYSCALL_NONE=y selected instead.
Inspect & investigate
No dedicated log. The active vsyscall mode is influenced at boot via the vsyscall= parameter in cat /proc/cmdline. Blocked vsyscall reads surface as segfaults in dmesg | grep -i -e vsyscall -e segfault or journalctl -k | grep -i vsyscall.
Remediation
No automated remediation: this is a kernel build-time choice, not a runtime tunable. Pavois's harden plan is empty. To fix it you must boot a kernel built with CONFIG_LEGACY_VSYSCALL_NONE=y instead of xonly. At runtime you can fully disable the page on a still-mapping kernel with the vsyscall=none boot parameter, but the compile-time NONE choice this rule prefers requires a different kernel.
Pavois applies this with its own harden engine, the plan below, not a shell script:
| resource | kernel_build |
|---|
pavois harden plan localwhere the target is local, a user@host SSH alias, or a container , Docs
Impact & precautions
Switching from XONLY to NONE removes the vsyscall page used as a compatibility shim by very old statically linked binaries (glibc older than ~2.14); modern software using the vDSO is unaffected. Audit for legacy static binaries before applying; if one is required, vsyscall=emulate is the (less-secure) compatibility fallback. Because the change requires a different kernel, keep the previous kernel entry in the bootloader.
Standards mapping
| Standard | Reference | Type | Version | Confidence |
|---|---|---|---|---|
| ANSSI BP-028 | R15 | direct | 2.0 | high |
Each reference is a cross-reference anchored in the upstream benchmark and cross-validated against the SCAP Security Guide and ansible-lockdown, not a claim of equivalence. Direct = a prescriptive, line-level requirement; supporting = an abstract control family (NIST) the check provides evidence toward. How to read a mapping.