Impossible OS Docs

Kernel Security Hardening

What is it?

Kernel security hardening is the set of CPU and kernel mitigations that make exploits harder: no-execute data, supervisor access controls, Spectre defenses, a hardened heap and stack, a read-only kernel image, and a release build that removes its own test surface. Some of it is fully active today. Several pieces exist as detected-but-disabled code waiting on one shared prerequisite, and one legacy system-call path has a known, unfixed flaw described below.

How does it work?

cpu_harden() runs on the BSP and on every AP. It sets EFER.NXE through cpu_enable_nx() in cpu_security.c, and vmm_apply_nx_policy() marks the non-code pages of the kernel image range no-execute. Firmware regions, MMIO, UEFI runtime memory and memory above the kernel image are skipped, so no-execute does not yet cover every mapping.

SMEP and SMAP have complete code paths, cpu_enable_smep() and cpu_enable_smap(), but neither turns its CR4 bit on: both first ask hv_supports_cr4_smep_smap(), which returns 0 because the shared boot page tables set the User bit on every kernel page, so enabling SMEP would fault on the kernel's own code. Serial reports SMEP: skipped and SMAP: skipped on every boot and every platform. The SMAP-bracketed copy_from_user() and copy_to_user() are ready for when it is enabled.

Kernel page-table isolation (KPTI) is in the same state: kpti_init() in kpti.c sets up a trampoline page and per-CPU kernel and user CR3 fields (kpti.h), but both CR3 values are the same and no entry path switches them. SMEP, SMAP and KPTI all wait on the same root cause: the kernel image shares its address range with user space, and moving it to the higher half is the prerequisite (kernel address space).

Spectre v2 mitigation is active. cpu_program_bsp_eibrs() sets IA32_SPEC_CTRL once per CPU when enhanced IBRS exists, and the Makefile builds with -mretpoline -mretpoline-external-thunk so CPUs without it get retpoline thunks instead of raw indirect branches. cpu_issue_ibpb() runs on cross-process context switches, and an lfence follows swapgs on entry from ring 3.

The kernel heap (heap.c) puts a cookie and redzones around every allocation, checks them on free under the heap spinlock, and stops with a heap-corruption bugcheck on a mismatch. Stack canaries use -fstack-protector-strong; canary_init() seeds the guard from RDRAND, with a TSC fallback, and __stack_chk_fail() in stack_canary.c is fatal.

The kernel image is write-xor-execute: kernel_wx_protect() and kernel_rodata_protect() in wx.c make .text and .rodata read-only before SMP starts and halt if that fails.

Kernel task, thread, user-thread, AP, interrupt and boot stacks have an unmapped guard page below them, installed by vmm_install_guard_page() in vmm.c. The table holds up to 640 entries (VMM_MAX_GUARD_PAGES in vmm.h), installation reports failure instead of dropping an entry, and the split and clear happen under one lock. A guard-page fault halts with a label naming the stack. Two ring-0 stacks are still unguarded: the ones task_create_user() and task_fork() allocate with kmalloc.

The KERNEL_TESTS build setting has two values. on, the default, links the full test surface, including a fault-injection system call. off removes src/kernel/test/ and every test-only hook, and check-release-symbols.sh compiles each file both ways and proves that no symbol gated on KERNEL_TESTS survives into the release build; a test-only helper defined unconditionally in a production file would not differ between the flavors, so its separate audit for those is advisory only.

What are its interfaces?

Interface Purpose
cpu_enable_nx() Enable no-execute on this CPU (cpu_security.c)
cpu_enable_smep(), cpu_enable_smap() SMEP and SMAP activation, currently skipped
copy_from_user(), copy_to_user() SMAP-bracketed user copies
cpu_program_bsp_eibrs(), cpu_issue_ibpb() Enhanced IBRS setup and the context-switch barrier
kpti_init() KPTI trampoline and CR3 fields, not yet switched (kpti.c)
kmalloc(), kfree() Cookie- and redzone-checked heap (heap.c)
canary_init(), __stack_chk_fail() Stack canary seed and failure handler (stack_canary.c)
kernel_wx_protect(), kernel_rodata_protect() Read-only kernel code and data (wx.c)
vmm_install_guard_page() Stack guard pages (vmm.c)
KERNEL_TESTS=on or off Build flavor (Makefile)

How do I use it?

Everything except the build flavor is always on; there is no boot.conf switch for no-execute, heap checks, canaries or W^X.

KERNEL_TESTS=off bash scripts/build.sh    # release flavor without the test surface
bash scripts/check-release-symbols.sh     # proves no flavor-gated test symbol reached the release image
bash scripts/test.sh SUITE=mm             # heap, W^X and guard-page tests
bash scripts/test.sh SUITE=security       # canary tests
bash scripts/test.sh SUITE=x86            # Spectre mitigation tests

Serial shows what actually turned on: the SMEP and SMAP skip lines, and [wx] lines reporting .text and .rodata read-only.

What is not implemented yet?

How does it compare with Windows 11 and Linux?

Both ship no-execute, SMEP and SMAP, KPTI, Spectre v2 mitigations, CET, heap and stack hardening, guard pages, KASLR and a read-only kernel image; Linux also has stackleak and KFENCE. Impossible OS matches them on the Spectre v2 core, heap cookies and redzones, stack canaries, the read-only image and a checked release build without the flavor-gated test surface, and partly on no-execute (kernel image only) and guard pages (not yet on user-process kernel stacks). SMEP, SMAP, KPTI and KASLR wait on the higher-half kernel move, and CET, kCFI, FORTIFY_SOURCE, stackleak and KFENCE are not built. Windows HVCI and VBS need a hypervisor layer this project does not have.

See also

Impossible OS is free software under the GPL-3.0-only license. Last updated Edit this page on GitHub