Detect stack corruption on calls to schedule()
Requires the kernel to be built with CONFIG_SCHED_STACK_END_CHECK=y so the end-of-stack canary is verified on every call to schedule().
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
This ensures no erroneous behaviour occurs which could result in data corruption or a sporadic crash at a later stage once the region is examined. CONFIG_SCHED_STACK_END_CHECK makes the kernel verify the stack canary at the end of the kernel stack every time schedule() is called; a deep recursion or a stack-overflowing exploit is caught immediately with a panic, turning a silent kernel-stack overflow (a corruption primitive attackers leverage) into a clean, contained failure.
What Pavois checks
Pavois reads the build options of the running kernel, grep '^CONFIG_SCHED_STACK_END_CHECK=' /boot/config-$(uname -r) or zcat /proc/config.gz, and requires y. Reading the live kernel's own config (keyed on uname -r) reflects exactly the booted kernel, which a package-name guess or generic file cannot.
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_SCHED_STACK_END_CHECK=|PAVOIS_NO_KERNEL_CONFIG)'") do
its('stdout') { should_not match(/PAVOIS_NO_KERNEL_CONFIG/) }
its('stdout') { should match(/^CONFIG_SCHED_STACK_END_CHECK=y$/) }
endHow to verify it is applied
Run grep '^CONFIG_SCHED_STACK_END_CHECK=' /boot/config-$(uname -r) (or zcat /proc/config.gz | grep CONFIG_SCHED_STACK_END_CHECK). Expected output:
CONFIG_SCHED_STACK_END_CHECK=y
Inspect & investigate
No routine log entry. When the check fires, the kernel logs Thread overran stack, or stack corrupted and panics, visible in dmesg / journalctl -k and any crash dump. Confirm the build flag with grep CONFIG_SCHED_STACK_END_CHECK /boot/config-$(uname -r).
Remediation
This is a kernel build-time option, so Pavois ships no automated harden step for it. It is satisfied by booting a distribution kernel compiled with CONFIG_SCHED_STACK_END_CHECK=y (the case for current Debian/Ubuntu/RHEL hardened kernels); a custom kernel must be rebuilt.
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
If absent, a kernel-stack overflow can corrupt adjacent memory silently and may be weaponized for exploitation instead of failing safe. Precautions: the per-schedule() check is extremely cheap and has no functional impact, so enabling it via a compliant kernel is low-risk. The only visible effect is that genuine stack overruns now produce an immediate panic rather than undefined behaviour, which is the desired outcome. No special lockout concern beyond the normal kernel switch.
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.