Subscribe to our RSS
Share this on Facebook
Share this on Twitter
Share this on LinkedIn

 

 

This article discusses the process by which your kernel’s configuration can be strengthened to protect against common security exploits. This is sometimes referred to as hardening, or specifically in this context, kernel configuration hardening.

 

Preface

A Linux kernel configuration is a file which defines all of the enabled (or disabled) options which are compiled in to your kernel. If you have not seen one before, they generally reside in the kernel’s build directory with a filename of “.config”. They are sometimes collapsed in to a defconfig (default config) file which only shows options which were not already selected by default.

This discussion will present many configuration tables in the form of:

Expected Conditional Architectures Kernel Versions Note
OPTION1=Y Architecture to which this option applys (X86 and/or ARM) Kernel versions in which OPTION1 exists A description of OPTION1
OPTION2=is not set Architecture to which this option applys (X86 and/or ARM) Kernel versions in which OPTION2 exists A description of OPTION2

In this case, it is recommended that the fictional option, OPTION1, is selected (CONFIG_OPTION1=y) in your kernel’s .config or defconfig file. It is also recommended that OPTION2 is disabled (CONFIG_OPTION2=is not set) in your kernel’s .config or defconfig file.

The option descriptions are sometimes difficult to interpret and may require further research. If you find an option that you’d like to know more information about, you may have to inspect the kernel source, search LWN, search Patchwork, or use your web search engine of choice.

 

Understanding Configuration Selections

When Linux kernel hardening, there are generally four categories of reason for which a configuration item may be enabled or disabled:

  • Adding an additional level of protection against a known exploit by enabling a configuration item. For example:
    • Mitigating Spectre attacks with CONFIG_RETPOLINE=y (which traps the processor’s speculative execution paths for indirect address calls by using a return trampoline).
  • Disabling a configuration item or subsystem which is known to be exploitable. For example:
    • Disabling the USB networking subsystem (USB_USBNET=is not set), so that applications using network-based IPC(Inter-Processor Communication) mechanisms may not be inadvertently exposed through a USB port.
    • Disabling /dev/mem access to physical memory (DEVMEM=is not set), so that physical memory cannot be easily modified.
  • Enabling general security strengthening features in the kernel (which may necessarily protect against a presently known attack). For example:
    • Reducing the risk of memory page leakage by enabling page poisoning (PAGE_POISONING=Y) to overwrite any potentially sensitive information upon freeing.
    • Enabling Security-Enhanced Linux
  • Disabling any unused kernel configuration options. If you don’t need it, disable it. As an added bonus, doing so may also improve your boot times.
Expected Conditional Architectures Kernel Versions Note
RETPOLINE=Y X86_32, X86_64 4.15-4.20, 5.0-5.17 Avoid speculative indirect branches in kernel (Spectre Mitigation)
USB_USBNET=is not set ARM, ARM64, X86_32, X86_64 2.6.22-2.6.39, 3.0-3.19, 4.0-4.20, 5.0-5.17 If enabled, this adds USB networking subsystem support to the kernel
DEVMEM=is not set ARM, ARM64, X86_32, X86_64 4.0-4.20, 5.0-5.17 Do not allow access to physical memory via /dev/mem
PAGE_POISONING=Y ARM, ARM64, X86_32, X86_64 4.6-4.20, 5.0-5.17 Fill the pages with poison patterns after free_pages() and verify the patterns before alloc_pages. The filling of the memory helps reduce the risk of information leaks from freed data. This must be enabled from the boot cmdline with page_poison=1

The latter two options can help to protect against exploits which have not yet been discovered or released into the public domain (e.g. zero-day exploits) by reducing your kernel’s exploitable attack surface.

Analyzing, understanding, and modifying the kernel configuration with these tasks in mind is not trivial. Furthermore, in a large project it may not be clear exactly who is using which kernel configuration option. This can result in iterative backstepping until you arrive at a final configuration which works for your entire team.

There’s also a maintenance burden once you have created your final configuration. When you upgrade your kernel, many of the configuration options will have been removed and renamed. This will require another assessment and configuration period. Keeping your kernel up to date is extremely important, as security features are continuously added and revised in newer kernels. Starting a project with a long term stable (LTS) kernel is recommended, as an LTS kernel provides smaller (and sometimes backported) version increments to resolve security flaws (e.g upgrading from 5.0.x to 5.0.y). Such patches are provided until the long term stable support period ends.

 

Cost-Benefit Analysis

Not all configuration items provide the same cost-benefit as others. To some extent, most options will have an impact in these key areas:

  • Compile Time
    • This is especially true for security features which are checked by the compiler during compile time. For example, GCC_PLUGIN_STACKLEAK=Y will block uninitialized stack variable attacks through the use of a compiler plugin which searches for and initializes such variables.
    • This should not be considered a concern. Added compilation time is well worth the added security.
  • Kernel Binary Size
    • This is usually not a concern. Adding and removing features may change the kernel size by a few megabytes, which is generally negligible on modern systems.
  • Boot time
    • For example, DM_CRYPT=y, which adds drive encryption capabilities to your kernel, will add additional non-neglibile boot time to your system. This is because it may require booting in to an Initial Ram Filesystem, retreiving your disk encryption key, and ultimately mounting the encrypted disk through the device mapper subsystem. All of these steps add time to the boot process.
  • Processor Load
    • This is perhaps the most concerning option and must be determined by trial and error. If every available security option is enabled on a slower ARM processor, there may be too much overhead to reliably run your processes (at a reasonable latency).
    • For example, erasing memory with PAGE_POISONING=Y will use CPU cycles. The amount of overhead added will be dependent upon factors such as your CPU and RAM speed.
Expected Conditional Architectures Kernel Versions Note
GCC_PLUGIN_STACKLEAK=Y ARM64, X86_32, X86_64 5.2-5.17 This blocks most uninitialized stack variable attacks, with the performance impact being driven by the depth of the stack usage, rather than the function calling complexity. The performance impact on a single CPU system kernel compilation sees a 1% slowdown.
DM_CRYPT=Y ARM, ARM64, X86_32, X86_64 2.6.4-2.6.39, 3.0-3.19, 4.0-4.20, 5.0-5.17 Filesystem Hardening (Block Level Encryption via dm-crypt) – Requires userspace support
PAGE_POISONING=Y ARM, ARM64, X86_32, X86_64 4.6-4.20, 5.0-5.17 Fill the pages with poison patterns after free_pages() and verify the patterns before alloc_pages. The filling of the memory helps reduce the risk of information leaks from freed data. This must be enabled from the boot cmdline with page_poison=1

Some configuration items will provide a better cost-benefit. Configuration items which have minimal impact on processor load are most valuable. For example, these add virtually no CPU overhead:

  • Disabling DEBUG_BUGVERBOSE, which will help ensure that sensitive backtrace information is not leaked upon a kernel BUG() condition.
  • Enabling ARCH_HAS_ELF_RANDOMIZE, which will make repeat exploits much more difficult by randomizing certain memory locations.

While these will add CPU overhead to some degree:

  • Enabling DEBUG_VIRTUAL will enable some sanity checking in virt_to_page translation at the cost of CPU cycles.
  • Enabling INIT_ON_ALLOC_DEFAULT_ON or INIT_ON_FREE_DEFAULT_ON will protect against heap memory leaks by erasing the regions after use, at the cost of erase time.
Expected Conditional Architectures Kernel Versions Note
DEBUG_BUGVERBOSE=is not set ARM, ARM64, X86_32, X86_64 2.6.9-2.6.39, 3.0-3.19, 4.0-4.20, 5.0-5.17 Make sure this is not enabled, as it could provide an attacker sensitive kernel backtrace information on BUG() conditions
ARCH_HAS_ELF_RANDOMIZE=Y ARM, ARM64, X86_32, X86_64 4.1-4.20, 5.0-5.17 Randomized locations for stack, mmap, brk, and ET_DYN
INIT_ON_FREE_DEFAULT_ON=Y ARM, ARM64, X86_32, X86_64 5.3-5.17 More expensive form of INIT_ON_ALLOC_DEFAULT_ON. The primary difference is that data lifetime in memory is reduced, as anything freed is wiped immediately, making live forensics or cold boot memory attacks unable to recover freed memory contents.
INIT_ON_ALLOC_DEFAULT_ON=Y ARM, ARM64, X86_32, X86_64 5.3-5.17 All page allocator and slab allocator memory will be zeroed when freed, eliminating many kinds of “uninitialized heap memory” flaws, especially heap content exposures.
DEBUG_VIRTUAL=Y ARM, ARM64, X86_32, X86_64 2.6.28-2.6.39, 3.0-3.19, 4.0-4.20, 5.0-5.17 Enable some costly sanity checks in virtual to page code. This can catch mistakes with virt_to_page() and friends.

Using multiple kernel configurations (development and production) can be an option. While it is wise to develop on a prototype system that most closely resembles a production system (as to not cause unforseen bugs in changing timing conditions and loads between configurations), some security options be too cumbersome to reasonably develop on. If you’re writing a kernel driver and you need to use a tracing tool or read a core dump, then certainly enable them while developing.

 

Categories of Linux Kernel Hardening

In the Timesys Kernel Hardening Analysis Tool, the kernel security options have been divided into various groups. This categorization is by no means a definitive separation; some options could be further categorized or applied to multiple categories.

Memory Protection

Memory exploits are classes of attack in which an entity is able to retrieve or modified privileged information about the system. These can be further categorized into:

  • Stack Overflow Protections: These are security features which seek to prevent access to and tampering of stack variables in memory. A stack canary (an arbitrary value sitting at the top of the stack, which, if modified, alerts the kernel of tampering) is sometimes mentioned in these protections.
Expected Conditional Architectures Kernel Versions Note
INIT_STACK_ALL_ZERO=Y ARM, ARM64, X86_32, X86_64 5.9-5.17 Initializes everything on the stack with a zero value. This is intended to eliminate all classes of uninitialized stack variable exploits and information exposures, even variables that were warned to have been left uninitialized. (Strongest, safest)
GCC_PLUGIN_ARM_SSP_PER_TASK=Y ARM 5.2-5.17 Generates a separate stack canary value for each task, so if one task’s canary value is leaked it does not cause all other tasks to become vulnerable.
STACKPROTECTOR=Y ARM, ARM64, X86_32, X86_64 4.18-4.20, 5.0-5.17 This option turns on the “stack-protector” GCC feature. This feature puts, at the beginning of functions, a canary value on the stack just before the return address, and validates the value just before actually returning. Stack based buffer overflows (that need to overwrite this return address) now also overwrite the canary, which gets detected and the attack is then neutralized via a kernel panic.
STACKPROTECTOR_STRONG=Y ARM, ARM64, X86_32, X86_64 4.18-4.20, 5.0-5.17 Adds the CONFIG_STACKPROTECTOR canary logic to additional conditions related to variable assignment.
STACKPROTECTOR_PER_TASK=Y ARM, ARM64 5.0-5.17 Use a different stack canary value for each task
VMAP_STACK=Y ARM64 4.9-4.20, 5.0-5.17 Enable this if you want the use virtually-mapped kernel stacks with guard pages. This causes kernel stack overflows to be caught immediately.
SCHED_STACK_END_CHECK=Y ARM, ARM64, X86_32, X86_64 3.18-3.19, 4.0-4.20, 5.0-5.17 Additional validation check on commonly targeted structure. Detect stack corruption on calls to schedule()
STACKLEAK_METRICS=is not set ARM64, X86_32, X86_64 5.2-5.17 If this is set, STACKLEAK metrics for every task are available in the /proc file system.
STACKLEAK_RUNTIME_DISABLE=is not set ARM64, X86_32, X86_64 5.2-5.17 If set, allows runtime disabling of kernel stack erasing
GCC_PLUGIN_STACKLEAK=Y ARM64, X86_32, X86_64 5.2-5.17 This blocks most uninitialized stack variable attacks, with the performance impact being driven by the depth of the stack usage, rather than the function calling complexity. The performance impact on a single CPU system kernel compilation sees a 1% slowdown.
  • Heap Overflow: These are security features which seek to prevent heap memory exposure and modification.
Expected Conditional Architectures Kernel Versions Note
STRICT_KERNEL_RWX=Y ARM, ARM64, X86_32, X86_64 4.11-4.20, 5.0-5.17 If this is set, kernel text and rodata memory will be made read-only, and non-text memory will be made non-executable. This provides protection against certain security exploits (e.g. executing the heap or modifying text)
SLAB_FREELIST_HARDENED=Y ARM, ARM64, X86_32, X86_64 4.14-4.20, 5.0-5.17 Harden slab freelist metadata: Many kernel heap attacks try to target slab cache metadata and other infrastructure. This options makes minor performance sacrifices to harden the kernel slab allocator against common freelist exploit methods. Some slab implementations have more sanity-checking than others. This option is most effective with CONFIG_SLUB.
SLAB_FREELIST_RANDOM=Y ARM, ARM64, X86_32, X86_64 4.7-4.20, 5.0-5.17 Randomizes the freelist order used on creating new pages. This security feature reduces the predictability of the kernel slab allocator against heap overflows.
COMPAT_BRK=is not set ARM, ARM64, X86_32, X86_64 2.6.25-2.6.39, 3.0-3.19, 4.0-4.20, 5.0-5.17 Do not disable heap randomization
INET_DIAG=is not set ARM, ARM64, X86_32, X86_64 2.6.14-2.6.39, 3.0-3.19, 4.0-4.20, 5.0-5.17 Do not allow INET socket monitoring interface. Assists heap memory attacks
  • User Copy Protection: These are security features which seek to prevent memory exploitation during kernel and userspace memory transfer transactions.
Expected Conditional Architectures Kernel Versions Note
HARDENED_USERCOPY=Y ARM, ARM64, X86_32, X86_64 4.8-4.20, 5.0-5.17 This option checks for obviously wrong memory regions when copying memory to/from the kernel (via copy_to_user() and copy_from_user() functions) by rejecting memory ranges that are larger than the specified heap object, span multiple separately allocated pages, are not on the process stack, or are part of the kernel text. This kills entire classes of heap overflow exploits and similar kernel memory exposures.
HARDENED_USERCOPY_FALLBACK=is not set ARM, ARM64, X86_32, X86_64 4.16-4.20, 5.0-5.15 This is a temporary option that allows missing usercopy whitelists to be discovered via a WARN() to the kernel log, instead of rejecting the copy, falling back to non-whitelisted hardened usercopy that checks the slab allocation size instead of the whitelist size.
HARDENED_USERCOPY_PAGESPAN=is not set ARM, ARM64, X86_32, X86_64 4.8-4.20, 5.0-5.17 When a multi-page allocation is done without __GFP_COMP, hardened usercopy will reject attempts to copy it. There are, however, several cases of this in the kernel that have not all been removed. This config is intended to be used only while trying to find such users.
HAVE_HARDENED_USERCOPY_ALLOCATOR=Y ARM, ARM64, X86_32, X86_64 4.8-4.20, 5.0-5.17 The heap allocator implements __check_heap_object() for validating memory ranges against heap object sizes.
  • Information exposure: Options which are selected to limit exposure to privileged information.
Expected Conditional Architectures Kernel Versions Note
X86_UMIP=Y X86_32, X86_64 5.5-5.17 If enabled, a general protection fault is issued if the SGDT, SLDT, SIDT, SMSW or STR instructions are executed in user mode. These instructions unnecessarily expose information about the hardware state.
PROC_PAGE_MONITOR=is not set ARM, ARM64, X86_32, X86_64 2.6.28-2.6.39, 3.0-3.19, 4.0-4.20, 5.0-5.17 Do not expose process memory utilization via /proc interfaces
PROC_VMCORE=is not set ARM, ARM64, X86_32, X86_64 2.6.37-2.6.39, 3.0-3.19, 4.0-4.20, 5.0-5.17 Do not export the dump image of crashed kernel
DEBUG_FS=is not set ARM, ARM64, X86_32, X86_64 2.6.11-2.6.39, 3.0-3.19, 4.0-4.20, 5.0-5.17 Do not enable debugfs, as it may expose vulnerabilities
  • Kernel Address Space Layout Randomization (KASLR): A security method by which kernel memory structures are randomized in order to prevent repeat or replay-style attacks.
Expected Conditional Architectures Kernel Versions Note
ARCH_HAS_ELF_RANDOMIZE=Y ARM, ARM64, X86_32, X86_64 4.1-4.20, 5.0-5.17 Randomized locations for stack, mmap, brk, and ET_DYN
RANDOMIZE_BASE=Y ARM64, X86_32, X86_64 4.7-4.20, 5.0-5.17 In support of Kernel Address Space Layout Randomization (KASLR), this randomizes the physical address at which the kernel image is decompressed and the virtual address where the kernel image is mapped, as a security feature that deters exploit attempts relying on knowledge of the location of kernel code internals.
RANDOMIZE_MEMORY=Y X86_64 4.8-4.20, 5.0-5.17 Randomizes the base virtual address of kernel memory sections (physical memory mapping, vmalloc & vmemmap). This security feature makes exploits relying on predictable memory locations less reliable.
SLAB_FREELIST_RANDOM=Y ARM, ARM64, X86_32, X86_64 4.7-4.20, 5.0-5.17 Randomizes the freelist order used on creating new pages. This security feature reduces the predictability of the kernel slab allocator against heap overflows.
GCC_PLUGIN_RANDSTRUCT=Y ARM, ARM64, X86_32, X86_64 4.13-4.20, 5.0-5.17 Randomizes layout of sensitive kernel structures

Reducing Attack Surface

These are configuration options which can be selected to reduce the potential for exposure to unknown zero-day attacks by limiting the attack surface as much as we can. These are options that reduce the amount of information exposure and compiled-firmware attack surface (Again: If you don’t need it, disable it).

  • Kernel Replacement Attacks: Methods in which a kernel binary could be replaced during runtime.
Expected Conditional Architectures Kernel Versions Note
HIBERNATION=is not set ARM, ARM64, X86_32, X86_64 2.6.23-2.6.39, 3.0-3.19, 4.0-4.20, 5.0-5.17 Do not support hibernation. Allows replacement of running kernel.
KEXEC=is not set ARM, ARM64, X86_32, X86_64 2.6.16-2.6.39, 3.0-3.19, 4.0-4.20, 5.0-5.17 Do not allow system to boot another Linux kernel
KEXEC_FILE=is not set ARM, ARM64, X86_32, X86_64 3.17-3.19, 4.0-4.20, 5.0-5.17 Do not allow system to boot another Linux kernel
  • Module Security Attacks: These are attacks which can be performed by loading a tainted, custom, module in to a system or maliciously modifying a pre-existing module’s memory. The mitigations for this mostly consist of restricting execution regions, making such regions read-only, and signature checking prior to loading modules.
Expected Conditional Architectures Kernel Versions Note
MODULES=is not set ARM, ARM64, X86_32, X86_64 You should not allow for modules to be loaded unless you have the proper signing and signature checks enabled. Allowing the kernel to load unsigned modules can be dangerous
STRICT_MODULE_RWX=Y ARM, ARM64, X86_32, X86_64 4.11-4.20, 5.0-5.17 Module text and rodata memory will be made read-only, and non-text memory will be made non-executable. This provides protection against certain security exploits (e.g. writing to text)
MODULE_SIG=Y ARM, ARM64, X86_32, X86_64 3.7-3.19, 4.0-4.20, 5.0-5.17 Enable module signature verification
MODULE_SIG_ALL=Y ARM, ARM64, X86_32, X86_64 3.9-3.19, 4.0-4.20, 5.0-5.17 Automatically sign all modules during modules_install (so we don’t have to do this manually)
MODULE_SIG_SHA512=Y ARM, ARM64, X86_32, X86_64 3.7-3.19, 4.0-4.20, 5.0-5.17 Sign modules with SHA-512 algorithm
MODULE_SIG_FORCE=Y ARM, ARM64, X86_32, X86_64 3.7-3.19, 4.0-4.20, 5.0-5.17 Require modules to be validly signed
DEBUG_SET_MODULE_RONX=Y ARM, ARM64, X86_32, X86_64 Varies depending on architecture Helps catch unintended modifications to loadable kernel module’s text and read-only data. It also prevents execution of module data.
  • Reducing Syscall Exposure: Syscalls are interfaces in which user-space and kernel-space can communicate and access each other. Some legacy syscalls may be exploitable and are generally not required on modern systems. Disabling syscalls when possible is a good way to reduce your attack surface.
Expected Conditional Architectures Kernel Versions Note
SECCOMP=Y ARM, ARM64, X86_32, X86_64 Varies depending on architecture This kernel feature is useful for number crunching applications that may need to compute untrusted bytecode during their execution. By using pipes or other transports made available to the process as file descriptors supporting the read/write syscalls, it’s possible to isolate those applications in their own address space using seccomp. Once seccomp is enabled via prctl(PR_SET_SECCOMP), it cannot be disabled and the task is only allowed to execute a few safe syscalls defined by each seccomp mode.
USELIB=is not set ARM, ARM64, X86_32, X86_64 4.5-4.20, 5.0-5.17 If enabled, this allows the libc5 and earlier dynamic linker usblib syscall. Should no longer be needed.
MODIFY_LDT_SYSCALL=is not set X86_32, X86_64 4.3-4.20, 5.0-5.17 Linux can allow user programs to install a per-process x86 Local Descriptor Table (LDT) using the modify_ldt(2) system call. This is required to run 16-bit or segmented code such as DOSEMU or some Wine programs. It is also used by some very old threading libraries. Enabling this feature adds a small amount of overhead to context switches and increases the low-level kernel attack surface. Disabling it removes the modify_ldt(2) system call.
LEGACY_VSYSCALL_NONE=Y X86_32, X86_64 4.4-4.20, 5.0-5.17 There will be no vsyscall mapping at all. This will eliminate any risk of ASLR bypass due to the vsyscall fixed address mapping. Attempts to use the vsyscalls will be reported to dmesg, so that either old or malicious userspace programs can be identified.
X86_VSYSCALL_EMULATION=is not set X86_32, X86_64 3.19, 4.0-4.20, 5.0-5.17 If set, this enables emulation of the legacy vsyscall page.
  • Security Policy Attacks: These are attacks which attempt to gain elevated (root) privileges within a system, generally through the use or execution of a misconfigured binary or file. Mitigations for this mostly rely on Linux Security Modules (LSMs) which extend discretionary access control (DAC) or implement mandatory access control (MAC, Security-Enhanced Linux).
Expected Conditional Architectures Kernel Versions Note
SECURITY=Y ARM, ARM64, X86_32, X86_64 2.5.50-2.5.75, 2.6.0-2.6.39, 3.0-3.19, 4.0-4.20, 5.0-5.17 This allows you to choose different security modules to be configured into your kernel.
SECURITY_YAMA=Y ARM, ARM64, X86_32, X86_64 3.4-3.19, 4.0-4.20, 5.0-5.17 This selects Yama, which extends DAC support with additional system-wide security settings beyond regular Linux discretionary access controls. Currently available is ptrace scope restriction. Like capabilities, this security module stacks with other LSMs. Further information can be found in Documentation/admin-guide/LSM/Yama.rst.
SECURITY_WRITABLE_HOOKS=is not set ARM, ARM64, X86_32, X86_64 4.12-4.20, 5.0-5.17 If SECURITY_SELINUX_DISABLE must be set, make sure this is not set. Subsequent patches will add RO hardening to LSM hooks, however, SELinux still needs to be able to perform runtime disablement after init to handle architectures where init-time disablement via boot parameters is not feasible. Introduce a new kernel configuration parameter CONFIG_SECURITY_WRITABLE_HOOKS, and a helper macro __lsm_ro_after_init, to handle this case.
SECURITY_SELINUX_DISABLE=is not set ARM, ARM64, X86_32, X86_64 2.6.6-2.6.39, 3.0-3.19, 4.0-4.20, 5.0-5.17 Do not allow NSA SELinux runtime disable
SECURITY_LOCKDOWN_LSM=Y ARM, ARM64, X86_32, X86_64 5.4-5.17 Enables the lockdown LSM, which enables you to set the lockdown=integrity or lockdown=confidentiality modes during boot. Integrity attempts to block userspace from modifying the running kernel, while confidentiality also restricts reading of confidential material.
SECURITY_LOCKDOWN_LSM_EARLY=Y ARM, ARM64, X86_32, X86_64 5.4-5.17 Enable lockdown LSM early in init
LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY=Y ARM, ARM64, X86_32, X86_64 5.4-5.17 The kernel runs in confidentiality mode by default. Features that allow the kernel to be modified at runtime or that permit userland code to read confidential material held inside the kernel are disabled.
SECURITY_SAFESETID=Y ARM, ARM64, X86_32, X86_64 5.1-5.17 SafeSetID is an LSM module that gates the setid family of syscalls to restrict UID/GID transitions from a given UID/GID to only those approved by a system-wide whitelist. These restrictions also prohibit the given UIDs/GIDs from obtaining auxiliary privileges associated with CAP_SET{U/G}ID, such as allowing a user to set up user namespace UID mappings.
SECURITY_LOADPIN=Y ARM, ARM64, X86_32, X86_64 4.7-4.20, 5.0-5.17 Any files read through the kernel file reading interface (kernel modules, firmware, kexec images, security policy) can be pinned to the first filesystem used for loading. When enabled, any files that come from other filesystems will be rejected. This is best used on systems without an initrd that have a root filesystem backed by a read-only device such as dm-verity or a CDROM.
SECURITY_LOADPIN_ENFORCE=Y ARM, ARM64, X86_32, X86_64 4.20, 5.0-5.17 If selected, LoadPin will enforce pinning at boot. If not selected, it can be enabled at boot with the kernel parameter “loadpin.enforce=1”.

 

System Architecture

Many security features are architecture specific because of a specific hardware level reason (differing instruction set, caches, branch predictors, and more) or merely because they have not been implemented on a specific architecture. Looking at DEBUG_SET_MODULE_RONX, we find that it was a relatively recent addition for ARM and ARM64 architectures.

Expected Conditional Architectures Kernel Versions Note
DEBUG_SET_MODULE_RONX=Y X86_32, X86_64 2.6.38-2.6.39, 3.0-3.19, 4.0-4.10 Helps catch unintended modifications to loadable kernel module’s text and read-only data. It also prevents execution of module data.
DEBUG_SET_MODULE_RONX=Y ARM64 3.18-3.19, 4.0-4.10
DEBUG_SET_MODULE_RONX=Y ARM 3.14-3.19, 4.0-4.10

Looking at the Spectre and Meltdown variants, there are differing options depending on architecture as well:

Expected Conditional Architectures Kernel Versions Note
HARDEN_BRANCH_PREDICTOR=Y ARM, ARM64 4.16-4.20, 5.0-5.17 (Spectre related) Speculation attacks against some high-performance processors rely on being able to manipulate the branch predictor for a victim context by executing aliasing branches in the attacker context. Such attacks can be partially mitigated against by clearing internal branch predictor state and limiting the prediction logic in some situations.
RETPOLINE=Y X86_32, X86_64 4.15-4.20, 5.0-5.17 Avoid speculative indirect branches in kernel (Spectre Mitigation)

 

Timesys Kernel Hardening Analysis Tool

This tool is available as part of the meta-vigishield layer, learn more about VigiShield here.

This Yocto-based tool can perform some security-minded analysis of your kernel configuration. The tool generates a report that shows the status of many configuration items which we have assessed as being security related.

The output from the Timesys Kernel Hardening Analysis Tool is formatted as a Comma Separated List (CSV). As an example, here are the first few lines from a sample report are:

Detected Kernel Version: 5.0.19
Detected Architecture: ARM
Detected configuration at: /mnt/Projects/Yocto/build/tmp/work-shared/qemuarm-uboot/kernel
Report Generated At: 2022-02-01 12:22:07.088485
Expected Conditional Status    Priority Kernel Versions Category Note
GCC_PLUGIN_RANDSTRUCT=Y FAILED 3 (High) 4.13-4.20, 5.0-5.17 gcc_plugin Randomizes layout of sensitive kernel structures
GCC_PLUGIN_ARM_SSP_PER_TASK=Y SKIPPED (Version mismatch) 3 (High) 5.2-5.17 gcc_plugin Generates a separate stack canary value for each task, so if one task’s canary value is leaked it does not cause all other tasks to become vulnerable.
GCC_PLUGIN_STRUCTLEAK=Y SKIPPED (Version mismatch) 3 (High) 5.2-5.17 gcc_plugin This plugin is available to identify and zero-initialize stack variables that may have passed through uninitialized
STACKPROTECTOR=Y PASSED 3 (High) 4.18-4.20, 5.0-5.17 stack_canary This option turns on the “stack-protector” GCC feature. This feature puts, at the beginning of functions, a canary value on the stack just before the return address, and validates the value just before actually returning. Stack based buffer overflows (that need to overwrite this return address) now also overwrite the canary, which gets detected and the attack is then neutralized via a kernel panic.
STACKPROTECTOR_STRONG=Y PASSED 3 (High) 4.18-4.20, 5.0-5.17 stack_canary Adds the CONFIG_STACKPROTECTOR canary logic to additional conditions related to variable assignment.
INIT_ON_ALLOC_DEFAULT_ON=Y SKIPPED (Version mismatch) 3 (High) 5.3-5.17 memory_protection All page allocator and slab allocator memory will be zeroed when freed, eliminating many kinds of “uninitialized heap memory” flaws, especially heap content exposures.
INIT_ON_FREE_DEFAULT_ON=Y SKIPPED (Version mismatch) 3 (High) 5.3-5.17 memory_protection More expensive form of INIT_ON_ALLOC_DEFAULT_ON. The primary difference is that data lifetime in memory is reduced, as anything freed is wiped immediately, making live forensics or cold boot memory attacks unable to recover freed memory contents.
STRICT_KERNEL_RWX=Y PASSED 4.11-4.20, 5.0-5.17 3 (High) memory_protection If this is set, kernel text and rodata memory will be made read-only, and non-text memory will be made non-executable. This provides protection against certain security exploits (e.g. executing the heap or modifying text)
(MODULE_SIG_FORCE=Y) OR (MODULES=is not set) FAILED
DESCRIPTION: MODULES FAILED 1 (Low) 2.5.45-2.5.75, 2.6.0-2.6.39, 3.0-3.19, 4.0-4.20, 5.0-5.17 module_security You should not allow for modules to be loaded unless you have the proper signing and signature checks enabled. Allowing the kernel to load unsigned modules can be dangerous
DESCRIPTION: MODULE_SIG_FORCE FAILED 3 (High) 3.7-3.19, 4.0-4.20, 5.0-5.17 module_security Require modules to be validly signed

In this case, GCC_PLUGIN_RANDSTRUCT was not enabled in the kernel configuration file, so the return status is, “FAILED.

GCC_PLUGIN_ARM_SSP_PER_TASK, GCC_PLUGIN_STRUCTLEAK, INIT_ON_ALLOC_DEFAULT_ON, STRICT_KERNEL_RWX, and INIT_ON_FREE_DEFAULT_ON are all options which were available after the 5.0.19 kernel version, so those have been skipped with a, “SKIPPED (Version mismatch)” message.

The rest of the options were set appropriately and passed (STACKPROTECTOR and STACKPROTECTOR_STRONG).

Modules are also enabled without any forced signature checking, so the OR conditional [(MODULE_SIG_FORCE=Y) OR (MODULES=is not set)] for that has also failed.

Click here to get a free guide containing the entire list of recommended kernel security configurations to consider for hardening.


Subscribe to our RSS
Share this on Facebook
Share this on Twitter
Share this on LinkedIn

 

Nathan Barrett-Morrison, Senior Embedded Systems Engineer, has 8+ years of experience in designing and debugging embedded systems. In the past, he has been a software technical lead on ultrasonic-based digital processing systems and direct thermal printing solutions. He specializes in providing customers with lower level board support packages, drivers, and numerous other services. Nathan holds a bachelor’s degree in Electrical and Computer Engineering from OSU (Ohio State University).

About Timesys

Timesys has extensive experience with embedded system development and lifecycle management. Timesys has been instrumental in working with global leader semiconductor manufacturers with smart, quick and quality solutions for highly complex systems with accelerated product innovation and multiple product variants.