Kernel protections

Block kexec

Starting with Ubuntu 14.04 LTS (Trusty Tahr), you can disable kexec via sysctl. We enable CONFIG_KEXEC in Ubuntu so end users can use kexec as desired. The new sysctl allows administrators to disable kexec_load. This is desirable in environments where you set CONFIG_STRICT_DEVMEM and modules_disabled, for example. When Secure Boot is in use, the system restricts kexec by default to only load appropriately signed and trusted kernels.

Block module loading

In Ubuntu 8.04 LTS (Hardy Heron) and earlier, you could remove CAP_SYS_MODULES from the system-wide capability bounding set. This would stop any new kernel modules from being loaded. This was another layer of protection to stop kernel rootkits from being installed. The 2.6.25 Linux kernel (Ubuntu 8.10) changed how bounding sets worked, and this functionality disappeared. Starting with Ubuntu 9.10 (Karmic Koala), you can block module loading again by setting “1” in /proc/sys/kernel/modules_disabled.

Regression tests for this are included in test-kernel-security.py.

Denylist rare protocols

Normally, the kernel allows all network protocols to be autoloaded on demand via the MODULE_ALIAS_NETPROTO(PF\_...) macros. Since many of these protocols are old, rare, or generally of little use to the average Ubuntu user and may contain undiscovered exploitable vulnerabilities, we have denylisted them since Ubuntu 11.04 (Natty Narwhal). These include: ax25, netrom, x25, rose, decnet, econet, rds, and af_802154. If you need any of these protocols, you can specifically load them via modprobe, or update the /etc/modprobe.d/blacklist-rare-network.conf file to remove the denylist entry.

Regression tests for this are included in test-kernel-security.py.

dmesg restrictions

dmesg (diagnostic messages) is a well-known command-line tool that displays the contents of the kernel ring buffer. This allows users to analyze kernel operations related to software or hardware and can be treated as sensitive information. It is commonly used for troubleshooting issues in those layers. As it exposes sensitive information, attackers frequently use it when developing exploits.

The kernel configuration option CONFIG_SECURITY_DMESG_RESTRICT sets the default value of the sysctl key kernel.dmesg_restrict:

sysctl kernel.dmesg_restrict

When set to 1, it restricts access to information from dmesg to privileged users with the CAP_SYSLOG capability.

Starting with Ubuntu 20.10 (Groovy Gorilla), we enable this configuration by default.

For older releases, starting with Ubuntu 12.04 LTS (Precise Pangolin), the configuration is available. You can set it to 1 with the following command on a running system (this doesn’t persist after a reboot):

sudo sysctl -w kernel.dmesg_restrict=1

Kernel address display restriction

Kernel addresses can be exposed via /proc and other interfaces. This is sensitive information as it reveals the locations of internal kernel structures and ends up being frequently used by attackers developing exploits. Many files and interfaces contain these addresses (such as /proc/kallsyms, /proc/modules, etc.).

The sysctl key kernel.kptr_restrict controls the restriction on exposing kernel addresses in such interfaces:

sysctl kernel.kptr_restrict

When set to 1, it restricts reporting kernel addresses to privileged users with the CAP_SYSLOG capability, replacing the actual address with 0s. Additionally, we made various files and directories that also expose kernel addresses readable only by the root user: /boot/vmlinuz*, /boot/System.map*, /sys/kernel/debug/, /proc/slabinfo.

Starting with Ubuntu 11.04 (Natty Narwhal), we set kernel.kptr_restrict to 1 by default.

Regression tests for this are included in test-kernel-security.py.

Kernel Address Space Layout Randomization

Kernel Address Space Layout Randomization (kASLR) is a security feature that randomizes the kernel’s base address in memory every time the system boots. This process ensures the location is unique for each startup, meaning two machines or even subsequent reboots of the same device are unlikely to share the same base address. Consequently, kASLR complicates exploits that depend on predictable memory locations, forcing them to first discover this randomized base address before they can target internal kernel symbols.

kASLR and userspace Address Space Layout Randomization (ASLR) share the same concept: invalidating attacks that rely on static, predetermined addresses for code and data structures. This forces an exploit to first overcome the challenge of discovering these randomized locations. The fundamental distinction between them lies in their scope and application. Userspace ASLR is applied on a granular, per-process basis each time an application launches to protect individual programs. In contrast, kASLR is applied monolithically to the entire operating system kernel once at system boot time to protect the core of the system as a whole.

Starting with Ubuntu 16.10 (Yakkety Yak), we enable kASLR by default.

For older releases, starting with Ubuntu 14.10 (Utopic Unicorn), the feature is available. You can enable it by specifying the kaslr option on the kernel command line parameters. Note that doing this in such releases will disable the ability to enter hibernation mode.

/dev/kmem disabled

/dev/kmem used to provide access to the kernel’s address space, but there is no modern use anymore beyond attackers using it to load kernel rootkits.

The kernel configuration option CONFIG_DEVKMEM controlled the existence of this special file.

We removed the configuration and the interface from the kernel upstream source code starting with version 5.13. In Ubuntu, this is reflected starting from Ubuntu 21.10 (Impish Indri).

For older releases with kernel versions supporting this special file, we set the kernel configuration CONFIG_DEVKMEM option to n to disable it by default.

Regression tests for this are included in test-kernel-security.py.

Kernel lockdown

Starting with Ubuntu 20.04 LTS (Focal Fossa), we enable the Linux kernel’s lockdown mode in integrity mode. This prevents the root account from loading arbitrary modules or BPF programs that can manipulate kernel data structures. Lockdown enforcement is tied to UEFI Secure Boot.

Kernel stack protector

The kernel stack protector is a security feature that detects memory corruption on the kernel stack before a function returns. It works by placing a small, random value known as a stack canary (or guard) on the stack between the local variables and the stored return address when a function is called. A typical stack smashing attack overwrites a function’s local variables and continues writing past them to overwrite the return address, aiming to redirect program execution to malicious code. Before the function returns, the stack protector checks if the canary value has been altered. If an overflow has occurred and overwritten the canary, the check will fail. If that happens, the kernel immediately panics or halts the system to prevent the attacker from gaining control. This effectively mitigates exploits that rely on corrupting the return address to achieve arbitrary code execution or privilege escalation.

The kernel configuration option CONFIG_CC_STACKPROTECTOR enables this feature.

We enable this configuration by default in all supported Ubuntu releases for most of the supported architectures (not available on RISC-V and System z (S390X)).

Regression tests for this are included in test-kernel-security.py.

Read-only data sections

This feature is a kernel hardening mechanism that protects static kernel data from unauthorized modification by enforcing strict read-only permissions after system initialization is complete. During boot, critical data sections like .rodata are necessarily writable to allow for setup and configuration. Once this phase ends, the feature alters the underlying memory page permissions to be read-only, a rule enforced by the hardware’s Memory Management Unit (MMU). This directly mitigates a common class of attacks used by kernel rootkits, which often attempt to overwrite static data such as function pointers or configuration tables to hijack system behavior. With this protection enabled, the MMU blocks any illicit write attempt to these memory regions. This triggers a hardware exception known as a page fault, resulting in the kernel triggering a kernel panic. This immediate and total system halt is a “fail-secure” response designed to instantly stop the undesired operation and prevent a silent, malicious compromise of the kernel.

The kernel configuration option CONFIG_STRICT_KERNEL_RWX enables this feature. (Before Ubuntu 17.10 (Artful Aardvark), the configuration option was called CONFIG_DEBUG_RODATA).

We enable this configuration by default in all supported Ubuntu releases.

Regression tests for this are included in test-kernel-security.py.

Module RO/NX

The Read-Only (RO) and No-eXecute (NX) feature extends kernel configuration option CONFIG_STRICT_KERNEL_RWX to include similar restrictions for loaded modules in the kernel. The feature maps a module’s executable code into read-only, executable pages while mapping its data into read-write, non-executable pages. This separation directly prevents attacks that rely on memory corruption, as it prevents an attacker from executing injected shellcode from a data section or from modifying the module’s existing code at runtime. The Memory Management Unit (MMU) checks the permissions on each memory access. Any attempt to violate these rules is immediately blocked by the hardware, which triggers a page fault exception resulting in the kernel triggering a kernel panic.

The kernel configuration option CONFIG_STRICT_MODULE_RWX enables this feature. (Before Ubuntu 17.10 (Artful Aardvark), the configuration option was called CONFIG_DEBUG_SET_MODULE_RONX).

We enable this configuration by default in all supported Ubuntu releases.

Regression tests for this are included in test-kernel-security.py.