Skip to content

Latest commit

 

History

History
470 lines (399 loc) · 29.9 KB

File metadata and controls

470 lines (399 loc) · 29.9 KB

KASLR and Kernel Memory Layout

Reference material on how Linux KASLR works, what it randomizes, and how the kernel virtual address space is laid out across architectures.

Table of Contents

Linux KASLR history and implementation

Not all architectures support KASLR (CONFIG_RANDOMIZE_BASE) or enable it by default:

Architecture KASLR Added Date Default On Notes
x86_32 v3.14 (8ab3820fd5b2) 2013-10-13 v4.12 (09e43968fc6c) Kconfig default y since v4.12
x86_64 v3.14 (8ab3820fd5b2) 2013-10-13 v4.12 (09e43968fc6c) Kconfig default y since v4.12
arm64 v4.6 (f80fb3a3d508) 2016-02-24 Yes (defconfig) Enabled in upstream arm64 defconfig
MIPS32 v4.7 (405bc8fd12f5) 2016-05-13 No Max offset 128 MiB (limited by KSEG0)
MIPS64 v4.7 (405bc8fd12f5) 2016-05-13 No Max offset 1 GiB
s390 v5.2 (b2d24b97b2a9) 2019-04-29 v5.2 Kconfig default y from initial commit
PowerPC32 v5.5 (2b0e86cc5de6) 2019-11-13 No BookE/e500 (PPC_85xx) only
LoongArch v6.3 (e5f02b51fa0c) 2023-02-25 Yes (defconfig) Enabled in upstream loongson3_defconfig
RISC-V64 v6.6 (84fe419dc757) 2023-07-22 No
arm32 Not supported
PowerPC64 Not supported
sparc Not supported
SuperH (sh) Not supported
m68k Not supported
MicroBlaze Not supported
OpenRISC Not supported

Even where KASLR is unsupported, disabled, or failed to randomize, the kernel's load address may still vary across boots: a bootloader (U-Boot, GRUB, the EFI stub, Coreboot, etc.) is free to place the image at whatever physical address suits the board's memory map. The kernel is not actively randomizing in that case, but the base is still unknown to a local user a priori. KASLD treats this case identically — the same inference engine narrows the bootloader-chosen base from observable evidence (dmesg landmarks, /proc/iomem, /sys facts, devicetree reservations) to a residual window, or to a single address where the evidence allows. See KASLR runtime states below for the distinction between disabled (kernel at the link-time default), unsupported (arch has no KASLR machinery), and randomization failed (boot stub tried but no random offset applied).

See also:

Default text base and KASLR alignment

When KASLR is disabled, the kernel loads at a fixed virtual address — the "default text base." This address is determined by the architecture's linker script, Kconfig options, or hardware memory map. When KASLR is enabled, the kernel is placed at default + N × IMAGE_ALIGN, where N is chosen randomly within the architecture's valid range. IMAGE_ALIGN is therefore the randomization granularity: each possible position is one "KASLR slot."

The table is the kernel's compile-time default — the address the image lands at when no relocation occurs. On relocating architectures (every supported arch except PowerPC64 and SPARC) the bootloader chooses the load address from the board's memory map. The table is the baseline against which the KASLR slide is measured, not the load address on every system.

The "default text base" here is the image base (_text) — the start of the kernel image, which is what KASLR aligns and what KASLD reports. The familiar _stext (start of the code section) sits a fixed head gap above _text: zero on most architectures (so _text == _stext), but non-zero where a header precedes the code (arm64's .head.text, 0x10000). KASLD solves the image base and shows _stext as a derived line only when the two differ; a leaked _stext (e.g. from /proc/kallsyms) is normalized back to the image base when it is consumed, so the slide is always measured against _text.

Architecture Default text base Derivation IMAGE_ALIGN KASLR slots Entropy
x86_64 0xffffffff81000000 __START_KERNEL_map + PHYSICAL_START (page_64_types.h) 2 MiB 504² ~9 bits
x86_32 0xc0000000 PAGE_OFFSET (3G/1G vmsplit default) 2 MiB 248² ~8 bits
arm64 0xffff800080000000³ KIMAGE_VADDR = _PAGE_END(VA_BITS_MIN) + module-region size (memory.h) 64 KiB⁴ ~1073M ~30 bits
arm32 0xc0008000 PAGE_OFFSET + TEXT_OFFSET (0x8000, from arch/arm/Makefile) No KASLR
MIPS32 0x80100400 KSEG0 (0x80000000) + 1 MiB + TEXT_OFFSET (0x400, from head.S) 64 KiB varies varies
MIPS64 0xffffffff80100400 CKSEG0 (0xffffffff80000000) + 1 MiB + TEXT_OFFSET (0x400) 64 KiB varies varies
s390 0x3FFE0100000 CONFIG_KERNEL_IMAGE_BASE + TEXT_OFFSET (1 MiB) 16 KiB ~131K ~17 bits
PowerPC32 0xc0000000 PAGE_OFFSET (3G/1G default); BookE only 16 KiB¹ varies varies
PowerPC64 0xc000000000000000 PAGE_OFFSET (Kconfig) No KASLR
LoongArch 0x9000000000200000 DMW1 (0x9000000000000000) + TEXT_OFFSET (2 MiB, from Makefile) 64 KiB varies varies
RISC-V64 0xffffffff80002000 KERNEL_LINK_ADDR (top 2 GiB of VA) + TEXT_OFFSET (0x2000, .head.text) 2 MiB 512² ~9 bits
RISC-V32 0xc0002000 PAGE_OFFSET + TEXT_OFFSET (0x2000) No KASLR

The "Derivation" column shows where each default address comes from. On most architectures the formula is PAGE_OFFSET + TEXT_OFFSET, where PAGE_OFFSET is the start of the kernel virtual address space (set by hardware mapping or Kconfig) and TEXT_OFFSET is the offset from the mapping base to the .text section entry point (set by the linker script or boot protocol). x86_64 is an exception: the kernel image virtual base (__START_KERNEL_map = 0xffffffff80000000) is separate from PAGE_OFFSET (the direct-map base), and PHYSICAL_START (16 MiB) is added for alignment with the physical load address.

¹ PowerPC32 defines a KASLR_VIRT_ALIGN (16 KiB) larger than its IMAGE_ALIGN (4 KiB): BookE KASLR places the text base on the 16 KiB grid, so 16 KiB is the KASLR slot granularity while 4 KiB is only the page alignment.

² The slot count is an upper bound. Every architecture's KASLR placement code refuses positions where the image would extend past the end of the randomization region, so the actual available slots are:

valid_slots = (range_size - kernel_size) / IMAGE_ALIGN

kernel_size is measured differently per architecture. On x86 (arch/x86/boot/compressed/kaslr.c), it is init_size from the boot header — the decompression buffer requirement, which is larger than the final loaded kernel size. On RISC-V (arch/riscv/mm/init.c), it is _end − _start — the actual in-memory kernel size with no overhead. On x86, MODULES_VADDR is defined as __START_KERNEL_map + KERNEL_IMAGE_SIZE with no gap, so the ceiling is hard.

³ The arm64 row is the VA_BITS_MIN = 48 case (4K/16K 4-level, plus 52-bit LVA) — the common one. Sub-48 configs place the image higher and randomize over a smaller window, since KIMAGE_VADDR = _PAGE_END(VA_BITS_MIN) + module-region size and _PAGE_END = -(1 << (VA_BITS_MIN − 1)): 4K 3-level (VA_BITS = 39, common on Android) → 0xffffffc080000000, 64K 2-level (42), 16K 3-level (47), and 16K 2-level (36, EXPERT-gated). KASLD detects the running VA_BITS with an mmap boundary probe (mmap_arm64_va_bits) and resolves the per-config text band and entropy accordingly.

⁴ The arm64 text base is aligned to EFI_KIMG_ALIGN (64 KiB on 4K/16K pages, 128 KiB on 64K pages), NOT the 2 MiB seed step. Under KASLR the EFI stub loads the image at 64 KiB alignment (weaker than the 2 MiB boot protocol), and the kernel grafts the low 2 MiB bits of that physical address into the virtual KASLR displacement so text can still be mapped with 2 MiB blocks. The result is a virtual text base that steps by 64 KiB, not 2 MiB, so 64 KiB is the alignment of _text (the slot count divides the window by 64 KiB). The 2 MiB seed step governs only the high bits of the displacement.

Architecture Max slots Approx. kernel_size Typical runtime slots Reduction
x86_64 504 init_size ≈ 70 MiB ~469 ~7%
x86_32 248 init_size ≈ 40 MiB ~228 ~8%
RISC-V64 512 _end−_start ≈ 30 MiB ~497 ~3%
s390 ~131K ≈ 70 MiB ~127K ~3%
arm64 ~1073M ≈ 50 MiB ~1073M <0.01%

On x86 and RISC-V, where total entropy is ~9 bits (~500 slots), a 3–8% reduction is material. On s390 (~17 bits) and arm64 (~25 bits) the effect is negligible.

KASLR runtime states

A KASLR-capable architecture can be in one of four runtime states on any given boot. The states have different exploitation implications and KASLD reports them distinctly:

State Where the kernel landed Slot entropy Bootloader entropy
Active One of valid_slots positions chosen uniformly Full (log2(valid_slots)) Subsumed by KASLR — placement is randomized within the bootloader-determined range
Disabled The arch's compile-time default text base, exactly 0 bits — fully predictable None — opt-out is honoured before the bootloader chooses
Unsupported Bootloader-determined physical address; virtual address is hardware-fixed (e.g. PAGE_OFFSET + TEXT_OFFSET on arm32) 0 bits — arch has no KASLR machinery Whatever entropy the bootloader's placement policy provides — often per-boot deterministic
Randomization failed Boot-stub- or firmware-deterministic, not the link-time default 0 bits — boot stub skipped the random offset Whatever entropy the deterministic fallback path provides — typically the lowest aligned slot the firmware allocator returns, identical across boots on the same hardware

The path a boot takes to each state — and the load-bearing disabled vs randomization failed distinction — at a glance:

KASLR runtime-state decision flow: whether the architecture has KASLR machinery, whether KASLR was opted out, and whether a boot entropy source was available select one of unsupported, disabled, randomization-failed, or active; three yield zero bits but land at different, non-interchangeable positions

The four states are entered by different mechanisms:

  • Active — the default for any KASLR-supporting arch when the entropy source is available and not opted out.

  • Disabled is reached by an explicit opt-out:

    • nokaslr on the kernel cmdline (every KASLR-supporting arch).
    • CONFIG_RANDOMIZE_BASE=n at build time.
    • Hibernation resume on x86 (the kernel must land at the same address as the snapshot, so KASLR is forced off — see arch/x86/boot/compressed/kaslr.c).
    • The kexec_file token on LoongArch (kernel loaded by kexec_file_load(2) honours the loader's chosen address).
    • The elfcorehdr= kdump handoff path on s390 (the panic kernel must land at a known address).
    • FDT without /chosen/kaslr-seed on riscv64 non-EFI boot (the kernel falls back to the link-time default; the EFI path goes through the randomization failed state instead).
  • Unsupported applies to architectures where the kernel build has no KASLR machinery: arm32, PowerPC64, RISC-V32, SPARC.

  • Randomization failed applies when the KASLR machinery ran but could not produce a random offset:

    • arm64 EFI stub: no EFI_RNG_PROTOCOL, no FDT kaslr-seed (dmesg: KASLR disabled due to lack of seed).
    • arm64: FDT remap failure during early init (dmesg: KASLR disabled due to FDT remapping failure).
    • s390 boot stub: CPU has no PRNG instruction (dmesg: KASLR disabled: CPU has no PRNG).
    • s390 boot stub: not enough memory to relocate (dmesg: KASLR disabled: not enough memory).
    • riscv64 EFI stub: the same shape as arm64 (EFI_RNG_PROTOCOL falls back to a deterministic firmware-allocated position when no random source is exposed). The riscv64 no-seed dmesg signal is not always emitted by the kernel; whether the engine enters the randomization failed or disabled state depends on which indicator the components observe.

The dmesg line in the third state begins with the same KASLR disabled prefix as a deliberate opt-out — distinguishing them requires inspecting the reason text. The kernel is still relocated by the boot stub but lands at a firmware-determined position rather than the link-time default, so consumers MUST NOT treat this signal as a pin-to-default. KASLD emits a distinct scalar fact (SF_VIRT_KASLR_RANDOMIZATION_FAILED + SF_PHYS_KASLR_RANDOMIZATION_FAILED versus SF_VIRT_KASLR_DISABLED + SF_PHYS_KASLR_DISABLED) and neither sets the summary's kaslr.disabled flag nor pins Q_VIRT_IMAGE_BASE or Q_PHYS_IMAGE_BASE from this signal.

Per-host fingerprintability: in the randomization failed state, the position is deterministic per (firmware, kernel build, hardware) tuple. An operator who has previously captured ground truth on the same machine can re-use the slide on subsequent boots without re-leaking — a substantively different security posture from active KASLR where each boot is independent.

Physical and virtual KASLR

Linux KASLR randomizes the kernel location in both physical memory (where the kernel image resides in RAM) and virtual memory (where the kernel is mapped in the address space). Depending on the architecture, these may be randomized together using a single offset (coupled) or independently using separate offsets (decoupled).

On architectures where physical and virtual randomization are coupled (i.e. the same offset), leaking either a physical or virtual kernel address trivially reveals the other. On architectures where they are decoupled, a physical address leak does not directly reveal the virtual address (and vice versa), providing stronger isolation.

Architecture Phys/Virt Relationship Since Notes
x86_32 Coupled v3.14 Virtual offset equals physical offset
arm64 Decoupled v4.6 EFI stub randomizes physical; kaslr_early_init randomizes virtual; linear map has limited entropy
MIPS32/64 Coupled v4.7 Single relocation offset; fixed kseg0 virt-to-phys mapping
x86_64 Decoupled v4.8 Separate find_random_phys_addr / find_random_virt_addr; also CONFIG_RANDOMIZE_MEMORY for memory sections
s390 Decoupled v5.2 Identity-mapped (virt = phys) through v6.7; v6.8+ moves kernel text to a separate high mapping randomized as normal KASLR, so KASLD models it decoupled (TEXT_TRACKS_DIRECTMAP=0); the identity/direct-map base can also be randomized via RANDOMIZE_IDENTITY_BASE, a debug-oriented option off by default (pinned to 0 on production kernels)
PowerPC32 Coupled v5.5 Same offset applied to both addresses
LoongArch Coupled v6.3 Single relocation offset; direct-mapped windows
RISC-V64 Virtual only v6.6 Only virtual address randomized; physical depends on bootloader

KASLD models this relationship with two orthogonal per-architecture flags — TEXT_TRACKS_DIRECTMAP (does kernel text slide with the linear map?) and DIRECTMAP_STATIC (is the compile-time direct-map projection sound at runtime?). The quadrant they form decides the inference strategy, and every supported architecture sits on its diagonal:

Architecture coupling quadrant: DIRECTMAP_STATIC against TEXT_TRACKS_DIRECTMAP, with each architecture placed in its cell

See also:

Kernel sections

The kernel virtual address space contains distinct sections (text, modules, direct map, etc.) mapped at different address ranges. KASLR randomizes the kernel text base address, but not all sections are randomized together — depending on the architecture, other sections may be at fixed addresses, use the same KASLR offset, or be randomized independently.

The map below shows the x86_64 virtual and physical address spaces, with the KASLD quantity (Q_*) that resolves each region annotated alongside it:

Kernel address-space map for x86_64: virtual and physical bands with the Q_* quantity that resolves each

Architecture Text ↔ Phys Text ↔ Direct map Text ↔ Modules Notes
x86_64 Independent Independent Independent Three separate randomizations (CONFIG_RANDOMIZE_MEMORY)
x86_32 Coupled Coupled Tracks lowmem, not text Single KASLR offset; the module region starts at VMALLOC_START, which is PAGE_OFFSET plus the runtime lowmem size plus 8 MiB
arm64 Independent Independent Bracketed (within 2 GiB of text) Separate phys/virt randomization; modules drawn from a window spanning the kernel image, so they move with it
arm32 Coupled Below PAGE_OFFSET, spills to vmalloc No KASLR; a dedicated 16 MiB window below PAGE_OFFSET, with CONFIG_ARM_MODULE_PLTS (default y) spilling overflow into vmalloc above it
MIPS32/64 Coupled Coupled (kseg0) Fixed module region Hardware-defined mapping
PowerPC32 Coupled Coupled Platform-dependent A dedicated window below PAGE_OFFSET on 8xx / book3s32; every other platform, 85xx included, allocates from the shared vmalloc window
PowerPC64 Coupled Shared VAS No KASLR
LoongArch64 Coupled Coupled Fixed module region Direct-mapped windows
RISC-V64 Virtual only Decoupled Coupled (shifts with kernel) Module region anchored to kernel _end; text ↔ directmap coupled on legacy pre-v5.10 kernels (no KASLR)
RISC-V32 Coupled Below PAGE_OFFSET (vmalloc) No KASLR; modules share the vmalloc window, which on sv32 is the 512 MiB below PAGE_OFFSET

On coupled architectures, all sections are at fixed offsets from each other: a physical address reveals the virtual text base via phys_to_directmap_virt(), the direct map is at a known offset (TEXT_OFFSET) from the text base, and modules are either at a fixed address or a constant offset from PAGE_OFFSET. A single leak from any section is sufficient to derive the others. On decoupled architectures like x86_64, each section is randomized independently — a physical address reveals nothing about the virtual text base, and the direct map base (virt_page_offset_base) is randomized separately.

RISC-V64 is notable: the module region is anchored to the kernel image (MODULES_VADDR = PFN_ALIGN(&_end) - SZ_2G, MODULES_END = PFN_ALIGN(&_start)), so modules shift with the randomized kernel rather than occupying a fixed region. See architecture.md → Cross-region derivation for how KASLD exploits this.

Virtual memory split (vmsplit)

On 32-bit systems, the 4 GiB virtual address space is divided between userspace and the kernel. The boundary — PAGE_OFFSET (also known as the "vmsplit") — determines where the kernel virtual address space begins.

The most common configuration is a 3G/1G split (PAGE_OFFSET=0xC0000000), but embedded systems and custom kernels may use different splits:

Split PAGE_OFFSET User / Kernel Notes
1G/3G 0x40000000 1 GiB / 3 GiB Rare
2G(opt)/2G 0x78000000 ~1.9 GiB / ~2.1 GiB x86_32 only
2G/2G 0x80000000 2 GiB / 2 GiB Common on embedded ARM
3G(opt)/1G 0xB0000000 ~2.75 GiB / ~1.25 GiB x86_32 only
3G/1G 0xC0000000 3 GiB / 1 GiB Default for most distros

The vmsplit affects nearly all kernel virtual address boundaries: the kernel text base, direct map, and (on some architectures) the module region all shift with PAGE_OFFSET. This means KASLR analysis, address validation, and memory layout interpretation depend on knowing the correct vmsplit.

Since KASLD is typically compiled on one system and deployed to another, the compile-time PAGE_OFFSET assumption may not match the target system. KASLD handles this at runtime: components that detect the actual PAGE_OFFSET (e.g. mmap_brute_vmsplit, boot_config) emit a pageoffset tagged result, and the orchestrator automatically adjusts all layout boundaries before performing validation and analysis.

Architecture Configurable vmsplit Config option Default
x86_32 Yes CONFIG_VMSPLIT_* 0xC0000000 (3G/1G)
arm32 Yes CONFIG_PAGE_OFFSET / CONFIG_VMSPLIT_* 0xC0000000 (3G/1G)
PowerPC32 Yes CONFIG_PAGE_OFFSET 0xC0000000 (3G/1G)
x86_64 No 0xFF00000000000000 (5-level) / 0xFFFF800000000000 (4-level)
arm64 No 0xFFF0000000000000 (52-bit VA)
MIPS32 No 0x80000000 (hardware kseg0)
MIPS64 No 0xFFFFFFFF80000000 (xkseg)
PowerPC64 No 0xC000000000000000
LoongArch64 No 0x9000000000000000
RISC-V32 No 0xC0000000
RISC-V64 No 0xFF60000000000000 (SV57)

See also:

Function Granular KASLR (FG-KASLR)

Function Granular KASLR (aka "finer-grained KASLR") patches for the 5.5.0-rc7 kernel were proposed in February 2020 but have not been merged as of 2026.

This optional non-mainline mitigation "rearranges your kernel code at load time on a per-function level granularity" and can be enabled with the CONFIG_FG_KASLR flag.

FG-KASLR ensures the location of kernel and module functions are independently randomized and no longer located at a constant offset from the kernel .text base.

On systems which support FG-KASLR patches (x86_64 from 2020, arm64 from 2023), this makes calculating offsets to useful functions more difficult and renders kernel pointer leaks significantly less useful.

However, some regions of the kernel are not randomized (such as symbols before __startup_secondary_64 on x86_64) and offsets remain consistent across reboots. Additionally, FG-KASLR randomizes only kernel functions, leaving other useful kernel data (such as modprobe_path and core_pattern usermode helpers) unchanged at a static offset.

As of January 2026, a prerequisite for landing FG-KASLR on x86 is under discussion: a proposal to link the relocatable x86_64 kernel as a PIE (Ard Biesheuvel). Earlier x86 FG-KASLR attempts were tied to the x86-specific kernel relocation format; recasting the kernel as an ordinary position-independent ELF would let FG-KASLR be implemented in the ELF domain and carry to other architectures (arm64, RISC-V, LoongArch) rather than staying x86-only. PIE linking by itself randomizes only the kernel base — every symbol keeps a fixed offset from .text — so it is a foundation for FG-KASLR, not FG-KASLR itself; the per-function reordering is a separate step layered on top.

See also:

Glossary

KASLR and kernel-memory-layout terms used across the docs and in KASLD's output. For KASLD's own engine and tool vocabulary (quantity, estimate, covering, rule, …), see architecture.md → Glossary.

  • slide — the per-boot offset between the kernel's compile-time default base and where it actually loaded. The quantity KASLR randomizes; reported as slide +0x…. See Default text base.
  • default text base — the fixed virtual address the kernel image loads at when KASLR is disabled; the baseline the slide is measured against. See Default text base.
  • image base (_text) — the start of the kernel image: the address KASLR aligns and KASLD solves. _stext (code-section start) sits a fixed head gap above it, zero on most architectures. See Default text base.
  • IMAGE_ALIGN / KASLR slot — the randomization granularity. The kernel lands at default + N × IMAGE_ALIGN; each candidate position is one slot. The readout's Align column carries it. See Default text base.
  • search space — how many slots a quantity could still be in, given the evidence. The readout states it against the set the row narrows (24 of 505), which is the brute-force cost of that row. See usage.md.
  • entropy — the number of random bits in the placement, log2(slots); the same fact as the search space, expressed as a logarithm, and shown as ~N bits under -v and in the machine formats. See Default text base.
  • runtime state — which of four conditions a KASLR-capable boot is in (randomized, disabled, …). See KASLR runtime states.
  • PAGE_OFFSET — the start of the kernel virtual address space; on 32-bit, the user/kernel boundary (the vmsplit). See vmsplit.
  • vmsplit — the division of a 32-bit address space between userspace and kernel, set by PAGE_OFFSET. See vmsplit.
  • directmap (direct / linear map) — the contiguous 1:1 mapping of physical RAM into the kernel virtual address space. See Kernel sections.
  • vmemmap — the virtual region holding the struct page array. See Kernel sections.
  • coupled / decoupled — whether physical and virtual KASLR share one offset (coupled — either leak reveals the other) or use independent offsets (decoupled). KASLD names this with two per-arch flags; see architecture.md → Glossary and Physical and virtual KASLR.
  • FG-KASLR — Function Granular KASLR: per-function reordering, so a single leak no longer implies a constant offset to other functions. See FG-KASLR.