Skip to content

bcoles/kasld

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

180 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

KASLD logo generated with Copilot (cropped)

Build Status Release License: MIT

Kernel Address Space Layout Derandomization (KASLD)

A tool for inferring the Linux kernel base address and physical memory layout as an unprivileged local user, for the purpose of bypassing Kernel Address Space Layout Randomization (KASLR). An orchestrator discovers and runs standalone leak components, then validates, cross-references, and summarizes the results with section-aware consensus analysis.

Supports:

  • x86 (i386+, amd64)
  • ARM (armv6, armv7, armv8)
  • MIPS (mipsbe, mipsel, mips64el)
  • PowerPC (ppc, ppc64)
  • RISC-V (riscv32, riscv64)
  • LoongArch (loongarch64)

Table of Contents

Usage

sudo apt install libc-dev make gcc binutils git
git clone https://github.com/bcoles/kasld
cd kasld
make run

Each component in the src/ directory is a standalone leak component using a different technique to retrieve or infer kernel addresses. The kasld orchestrator discovers and executes all components, displays results in real-time, and produces a section-aware summary with validated addresses grouped by kernel section (text, modules, direct map, etc).

After building, the build/<arch>/ directory is self-contained and can be deployed to a target system:

build/<arch>/
  kasld              <- run this
  components/        <- leak components

Modern fully-patched systems with kernel.dmesg_restrict=1, kernel.kptr_restrict=1, and kernel.perf_event_paranoid=2 (or higher) are expected to return limited results. For testing purposes, the extra/weaken-kernel-hardening script can temporarily relax these settings (requires root).

Example Output

The following is example output from a default Debian 13 (x86-64) system:

Click to expand

     ▄█   ▄█▄    ▄████████    ▄████████  ▄█       ████████▄
    ███ ▄███▀   ███    ███   ███    ███ ███       ███   ▀███
    ███▐██▀     ███    ███   ███    █▀  ███       ███    ███
   ▄█████▀      ███    ███   ███        ███       ███    ███
  ▀▀█████▄    ▀███████████ ▀███████████ ███       ███    ███
    ███▐██▄     ███    ███          ███ ███       ███    ███
    ███ ▀███▄   ███    ███    ▄█    ███ ███▌    ▄ ███   ▄███
    ███   ▀█▀   ███    █▀   ▄████████▀  █████▄▄██ ████████▀
    ▀                                   ▀ v0.1.0

Kernel release:               6.12.38+deb13-amd64
Kernel version:               #1 SMP PREEMPT_DYNAMIC Debian 6.12.38-1 (2025-07-16)
Kernel arch:                  x86_64

kernel.kptr_restrict:         0
kernel.dmesg_restrict:        1
kernel.panic_on_oops:         0
kernel.perf_event_paranoid:   3

Readable /var/log/dmesg:      no
Readable /var/log/kern.log:   no
Readable /var/log/syslog:     no
Readable DebugFS:             no
Readable /boot/System.map:    yes
Readable /boot/config:        yes

Running 49 components...
[####################] 100%  49/49  11.1s

========================================
 Results
========================================

  Kernel text (virtual)     0xffffffffa7a00000  (1 source)
  Physical DRAM             0x0000000000000000 - 0x0000000080000000  (2.0 GiB, 7 sources)
  Physical MMIO             0x00000000000c0000 - 0x00000000febfffff  (4.0 GiB, 2 sources)

----------------------------------------
KASLR analysis:
  Virtual text base:    0xffffffffa7a00000
  Default text base:    0xffffffff81000000
  KASLR slide:          +648019968 (618.0 MiB)
  KASLR text entropy:   8 bits (504 slots of 0x200000)
  Observed slot index:  309 / 504

----------------------------------------
Virtual memory layout (decoupled):

  0xffffffffffffffff
  +------------------------------------------------------------------+
  |  modules                                                         |
  |  (no leak)                                                       |
  +------------------------------------------------------------------+
  0xffffffffc0000000
  +------------------------------------------------------------------+
  |  kernel text                                                     |
  |    0xffffffffa7a00000                                            |
  +------------------------------------------------------------------+
  0xffffffff80000000
  +------------------------------------------------------------------+
  |                                                                  |
  |  ...  128.0 TiB                                                  |
  |                                                                  |
  +------------------------------------------------------------------+
  |  direct map                                                      |
  |  (no leak)                                                       |
  +------------------------------------------------------------------+
  0xffff800000000000

Physical memory layout:

  0x00000000febfffff
  +------------------------------------------------------------------+
  |  0x00000000febfffff  [mmio] sysfs_pci_resource:hi                |
  |  0x0000000080000000  [dram] proc-zoneinfo:hi                     |
  |  0x000000007fffffff  [dram] sysfs_firmware_memmap:hi             |
  |  0x000000007c944000  [dram] sysfs_vmcoreinfo                     |
  |  0x00000000000c0000  [mmio] sysfs_pci_resource:lo                |
  |  0x0000000000001000  [dram] proc-zoneinfo:lo                     |
  |  0x0000000000000000  [dram] sysfs_firmware_memmap:lo             |
  +------------------------------------------------------------------+
  0x0000000000000000

Building

A compiler which supports the _GNU_SOURCE macro is required due to use of non-portable code (MAP_ANONYMOUS, getline(), popen(), ...).

make              # build kasld + components
make run          # build and run
make test         # build and run unit tests
make cross        # cross-compile for all supported architectures
make install      # install to /usr/local (PREFIX=/usr/local)
make uninstall    # remove installed files
make clean        # remove build directory
make help         # show all targets and options

Command-line options:

-j, --json      Machine-readable JSON output
-1, --oneline   Single-line summary output
-m, --markdown  Markdown table output
-c, --color     Colorize text output (auto-detected for TTYs)
-v, --verbose   Show component output
-V, --version   Print version and exit
-h, --help      Show this help

KASLD can be cross-compiled with make by specifying the appropriate compiler (CC). Static linking is applied automatically when cross-compiling:

make CC=aarch64-linux-musl-gcc

Build all supported cross-compilation targets (toolchains must be in PATH):

make cross

Configuration

Architecture-specific kernel memory layout constants are defined in kasld.h. The default values should work on all systems, but may need to be adjusted for very old kernels, embedded devices, or systems with unusual configurations.

The orchestrator automatically aligns leaked addresses to KERNEL_ALIGN boundaries and adjusts for TEXT_OFFSET. If a component detects a non-default PAGE_OFFSET at runtime (e.g. on a 32-bit system with a 2G/2G vmsplit), the orchestrator adjusts all layout boundaries before validation.

Refer to the comment headers in kasld.h for documentation of each configuration option.

Architecture and Design

KASLD's architecture is a simple contract: each component is a standalone executable that probes one data source and prints tagged lines to stdout. The orchestrator discovers, runs, and post-processes components automatically — no registration, no linking, no Makefile changes.

Component model

The orchestrator runs each component as an isolated child process:

  1. fork() + execl() — the component runs in its own process group
  2. stdout and stderr are merged into a single pipe back to the orchestrator
  3. The orchestrator reads lines from the pipe, capturing tagged lines as results and (in verbose mode) printing all output
  4. A per-component timeout (default: 30 seconds, configurable via --timeout) kills the component and its children if it does not exit in time
  5. The exit code signals the component's relationship with its data source (see Exit code convention below). Tagged lines emitted before exit (or timeout) are always captured.

This model means a component that segfaults, hangs, or exits with an error does not affect other components or the orchestrator.

Tagged lines are parsed, aligned to the architecture's KERNEL_ALIGN boundary, validated against expected address ranges, assigned a method label (exact, parsed, timing, or heuristic), and grouped by section for consensus analysis. Components do not need to align addresses or validate ranges — report the raw leaked value and the orchestrator handles the rest.

Phases

Components run in three phases, determined by name:

Phase Purpose Components
1 — Discovery PAGE_OFFSET detection, KASLR state, exact addresses boot-config, default, dmesg_kaslr-disabled, proc-cmdline, proc-config, proc-cpuinfo, proc-kallsyms
2 — Inference All remaining filesystem/interface parsers Everything not in Phase 1 or Phase 3
3 — Probing Side-channels, timing, brute-force databounce, echoload, entrybleed, mmap-brute-vmsplit, prefetch, kernelsnitch

The orchestrator runs apply_layout_adjustments() between each phase, propagating PAGE_OFFSET discoveries and revalidating all prior results. Phase 3 is skipped entirely when KASLR is detected as disabled.

New components are placed in Phase 2 by default — no configuration needed. To assign a component to Phase 1 or Phase 3, add its name to the phase_discovery[] or phase_probing[] array in orchestrator.c.

Cross-section derivation

The kernel virtual address space contains distinct sections (text, modules, direct map) at different address ranges. On some architectures these are at fixed offsets from each other (coupled), so a leak from one section can derive addresses in another. KASLD exploits this at two levels: components can emit derived results directly, and the orchestrator derives centrally after collecting all results.

Component-level derivation

Components that leak a physical address can convert it to a direct-map virtual address using phys_to_virt(), guarded by #if !PHYS_VIRT_DECOUPLED so the derivation is compiled out on decoupled architectures (x86_64, arm64, RISC-V 64-bit). This produces an additional data point alongside the raw physical result.

Orchestrator derivation rules

The orchestrator implements cross-section derivation rules in compute_derived_addrs(). All derived addresses are aligned to KERNEL_ALIGN (e.g., 2 MiB on x86_64).

On coupled architectures (x86_32, arm32, MIPS, PowerPC, LoongArch, riscv32), the KASLR slide is a single offset applied uniformly, so a single address from any section is sufficient to derive all others:

Given Derives Formula
Physical text Virtual text (ptext - PHYS_OFFSET + PAGE_OFFSET + TEXT_OFFSET) & ~(ALIGN-1)
Virtual text Physical text vtext - PAGE_OFFSET + PHYS_OFFSET
PAGE_OFFSET (runtime) Default text base PAGE_OFFSET + TEXT_OFFSET

PAGE_OFFSET, PHYS_OFFSET, and TEXT_OFFSET are compile-time constants (or runtime-detected in the case of a non-default vmsplit).

On decoupled architectures (x86_64, arm64, riscv64), physical and virtual addresses are randomized independently, so physical results cannot derive virtual text. The orchestrator prints a note when physical results exist that would have been derivable on a coupled system.

RISC-V64 is a special case among decoupled architectures: its module region is anchored to the kernel image, so module addresses provide an additional derivation path:

Given Derives Formula
Lowest module address Kernel _end estimate vmod_lo + 2 GiB
Kernel _end estimate Text base range (_end - 64 MiB) .. (_end - 4 MiB), aligned to KERNEL_ALIGN
DRAM + module range Virtual text (confirmed) Physical rule above, validated against module-derived range

When both a physical DRAM address and a module address are available, the orchestrator cross-references them: if the physically-derived text base falls within the module-derived range, it is emitted as a high-confidence result.

Kernel version detection

Some techniques (e.g., EntryBleed) have been mitigated in specific mainline kernel releases. It would seem natural to check uname -r and skip components that target patched vulnerabilities.

KASLD deliberately does not do this. Distribution kernels (Ubuntu, Debian, RHEL, SUSE, etc.) backport security fixes extensively, and their version numbers do not correspond to the mainline release where a fix appeared:

  • Ubuntu ships 4.4.0-xxx and 4.15.0-xxx LTS kernels that contain backported fixes from mainline 5.x and 6.x.
  • RHEL ships 3.10.0-xxx kernels with fixes from mainline 4.x and 5.x.
  • A kernel reporting 6.8.0 may lack a fix from mainline 6.3, or include a fix from mainline 6.12, depending on the distributor.

Version-based gating would be wrong in both directions: skipping a technique on a kernel that is actually vulnerable (false negative), or running a technique on a kernel that has already been patched (false positive).

Instead, KASLD treats each component as its own detector. Components probe the actual kernel — they either produce a result or they don't. Side-channel components that target patched vulnerabilities will time out (bounded by --timeout, default 30 seconds) or exit with no output, which the orchestrator handles gracefully. Filesystem-based components that lack access typically fail in under a second.

This design means KASLD is correct on any kernel — mainline, distro, custom, or embedded — without maintaining per-component version ranges that would be inaccurate for most real-world deployments.

Writing a component

Tagged line protocol

Components communicate results to the orchestrator via tagged lines on stdout:

<type> <section> <addr> <label>
Field Format Description
type Single char: V, P, or D V = virtual address, P = physical address, D = default (KASLR-disabled indicator)
section String token (no spaces) Which kernel memory section the address belongs to (see table below)
addr 0x + 16 hex digits The leaked address, zero-padded (e.g., 0xffffffff81000000)
label Free-form text Human-readable identifier, typically the component name

Example output from a component:

[.] trying /proc/kallsyms ...
V text 0xffffffff81000000 proc-kallsyms

The orchestrator ignores any line that does not begin with V, P, or D followed by a space. This means components can freely print diagnostic messages (progress, errors, explanations) to stdout — only tagged lines are captured as results.

A component may emit zero, one, or multiple tagged lines. The orchestrator processes each independently.

Sections

Constant String Use when
KASLD_SECTION_TEXT text Address falls in the kernel text (.text) region
KASLD_SECTION_MODULE module Address is in the loadable module region
KASLD_SECTION_DIRECTMAP directmap Address is in the direct-map (linear mapping) region
KASLD_SECTION_DATA data Address is in the kernel data section
KASLD_SECTION_DRAM dram Physical DRAM address (use with type P)
KASLD_SECTION_MMIO mmio Physical MMIO address (use with type P)
KASLD_SECTION_PAGEOFFSET pageoffset The PAGE_OFFSET value itself (use with type V)
KASLD_SECTION_NONE - No specific section / default indicator

Labels

The label field identifies the source of the result. Convention:

  • Simple label: the component name (proc-kallsyms, sysfs_vmcoreinfo)
  • Qualified label: component:qualifier when a component emits multiple results from different derivations (sysfs_vmcoreinfo:directmap)
  • Special labels: default:text (default kernel text address), default:unsupported (KASLR not supported on this architecture)

Exit code convention

Components signal their outcome to the orchestrator via exit code:

Exit code Constant Meaning
0 Ran successfully (results, if any, are in tagged output)
69 KASLD_EXIT_UNAVAILABLE Data source or hardware feature not present on this system
77 KASLD_EXIT_NOPERM Access denied to data source

The orchestrator classifies each component's outcome using this priority:

  1. SUCCESS — component emitted at least one tagged line
  2. TIMEOUT — component was killed by the timeout
  3. ACCESS_DENIED — exit code 77
  4. UNAVAILABLE — exit code 69
  5. NO_RESULT — ran successfully but found nothing

The exit code answers "what was your relationship with your data source?" — not "did you find results". A component that accessed its data source and found no matching data should exit 0, not 69 or 77. The orchestrator already knows whether results were found from the tagged output.

The constants are defined in src/include/kasld_internal.h and follow the <sysexits.h> convention (EX_UNAVAILABLE = 69, EX_NOPERM = 77).

Minimal component

// src/components/my-leak.c
#include "include/kasld.h"
#include <stdio.h>
#include <stdlib.h>

int main(void) {
  unsigned long addr;

  /* ... probe a data source ... */
  addr = 0; /* replace with actual leak logic */

  if (!addr) {
    printf("[-] no kernel address found via my-leak\n");
    return 0;
  }

  printf("leaked kernel text address: 0x%lx\n", addr);
  kasld_result(KASLD_ADDR_VIRT, KASLD_SECTION_TEXT, addr, "my-leak");
  return 0;
}

Place the file in src/components/. Run make — the build system automatically discovers all .c files in that directory and compiles each into a standalone binary under build/<arch>/components/. No Makefile edits required.

The component can also be run directly:

$ ./build/x86_64-linux-musl/components/my-leak
leaked kernel text address: 0xffffffff81000000
V text 0xffffffff81000000 my-leak

Components that leak a physical address should also emit a derived direct-map virtual address on coupled architectures:

kasld_result(KASLD_ADDR_PHYS, KASLD_SECTION_DRAM, phys, "my-leak");

#if !PHYS_VIRT_DECOUPLED
unsigned long virt = phys_to_virt(phys);
kasld_result(KASLD_ADDR_VIRT, KASLD_SECTION_DIRECTMAP, virt,
             "my-leak:directmap");
#endif

The #if guard compiles out the derivation on decoupled architectures (x86_64, arm64, RISC-V 64-bit) where physical addresses cannot reveal virtual text. See Cross-section derivation for details.

API reference

The complete component API spans two headers: src/include/kasld.h (results, address layout) and src/include/kasld_internal.h (exit codes).

Symbol Purpose
kasld_result(type, section, addr, label) Emit a tagged result line
KASLD_ADDR_VIRT, KASLD_ADDR_PHYS, KASLD_ADDR_DEFAULT Type characters
KASLD_SECTION_TEXT, KASLD_SECTION_DRAM, ... Section strings
KASLD_EXIT_UNAVAILABLE Exit code 69: feature/hardware not present
KASLD_EXIT_NOPERM Exit code 77: access denied
KERNEL_TEXT_DEFAULT Default (non-randomized) kernel text base
KERNEL_VAS_START, KERNEL_VAS_END Kernel virtual address space bounds
KERNEL_BASE_MIN, KERNEL_BASE_MAX Plausible kernel text range
PAGE_OFFSET Direct-map base (compile-time default)
PHYS_OFFSET Physical-to-virtual offset
PHYS_VIRT_DECOUPLED 1 if phys/virt KASLR are independent
phys_to_virt(addr) Convert physical to direct-map virtual
virt_to_phys(addr) Convert direct-map virtual to physical

KASLR and Kernel Memory Layout

Function Offsets

As the entire kernel code text is mapped with only the base address randomized, a single kernel pointer leak can be used to infer the location of the kernel virtual address space and offset of the kernel base address.

Offsets to useful kernel functions (commit_creds, prepare_kernel_cred, etc) from the base address can be pre-calculated on other systems with the same kernel - an easy task for publicly available kernels (ie, distro kernels).

Function offsets may also be retrieved from various file system locations (/proc/kallsyms, vmlinux, System.map, etc) depending on file system permissions. jonoberheide/ksymhunter automates this process.

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-01-01).

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.

See also:

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

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 × KERNEL_ALIGN, where N is chosen randomly within the architecture's valid range. KERNEL_ALIGN is therefore the randomization granularity: each possible position is one "KASLR slot."

Architecture Default text base Derivation KERNEL_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 (memory.h); module region size determines offset from _PAGE_END 2 MiB¹ ~33M ~25 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 0xffffffff80000000 KERNEL_LINK_ADDR (top 2 GiB of VA) 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.

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 Coupled (identity) v5.2 1:1 virtual = physical mapping
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

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.

Architecture Text ↔ Phys Text ↔ Direct map Text ↔ Modules Notes
x86_64 Independent Independent Independent Three separate randomizations (CONFIG_RANDOMIZE_MEMORY)
x86_32 Coupled Coupled Fixed module region Single KASLR offset
arm64 Independent Independent Fixed module region Separate phys/virt randomization
arm32 Coupled Fixed (PAGE_OFFSET - 16M) No KASLR
MIPS32/64 Coupled Coupled (kseg0) Fixed module region Hardware-defined mapping
PowerPC32 Coupled Coupled Fixed (PAGE_OFFSET - 256M)
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 Same as PAGE_OFFSET No KASLR

On coupled architectures, all sections are at fixed offsets from each other: a physical address reveals the virtual text base via phys_to_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 tells you nothing about the virtual text base, and the direct map base (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 Orchestrator derivation rules 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:

KASLR Bypass Techniques

KASLR bypass techniques broadly fall into several categories: reading kernel pointers or memory layout details from filesystem interfaces, exploiting microarchitectural or software side-channels, leaking addresses through syscalls and kernel interfaces, brute-forcing memory layout constraints, taking advantage of weak randomization entropy, leveraging patched kernel info leak bugs, and using arbitrary read primitives.

Filesystem Leaks

The kernel exposes a variety of information through pseudo-filesystems (/proc, /sys), log files (/var/log), and boot configuration files (/boot, /proc/config.gz) that can reveal kernel pointers or memory layout details to unprivileged users.

System Logs

Kernel and system logs (dmesg / syslog) offer a wealth of information, including kernel pointers and the layout of virtual and physical memory.

Many KASLD components search the kernel message ring buffer for kernel addresses. The following KASLD components read from dmesg and /var/log/dmesg:

Historically, raw kernel pointers were frequently printed to the system log without using the %pK printk format.

Bugs which trigger a kernel oops can be used to leak kernel pointers by reading the associated backtrace from system logs (on systems with kernel.panic_on_oops = 0).

For testing purposes, a backtrace can be forced using SysRq (requires root):

echo l > /proc/sysrq-trigger

This prints a backtrace of all CPUs to the kernel log, which the dmesg_backtrace component will then parse. The SysRq l command requires kernel.sysrq to include bit 4 (dump-backtrace), which is enabled by default on most distro kernels (kernel.sysrq = 1 enables all commands).

Most modern distros ship with kernel.dmesg_restrict enabled by default to prevent unprivileged users from accessing the kernel debug log. Similarly, grsecurity hardened kernels support kernel.grsecurity.dmesg to prevent unprivileged access.

System log files (ie, /var/log/syslog) are readable only by privileged users on modern distros. On Debian/Ubuntu systems, users in the adm group also have read permissions on various system log files in /var/log/:

$ ls -la /var/log/syslog /var/log/kern.log /var/log/dmesg
-rw-r----- 1 root   adm 147726 Jan  8 01:43 /var/log/dmesg
-rw-r----- 1 syslog adm    230 Jan 15 00:00 /var/log/kern.log
-rw-r----- 1 syslog adm   8322 Jan 15 04:26 /var/log/syslog

Typically the first user created during installation of an Ubuntu system is a member of the adm group and will have read access to these files.

Additionally, an initscript bug present from 2017-2019 caused the /var/log/dmesg log file to be generated with world-readable permissions (644) and may still be world-readable on some systems.

DebugFS

Various areas of DebugFS (/sys/kernel/debug/*) may disclose kernel pointers.

DebugFS is no longer readable by unprivileged users by default since kernel version v3.7-rc1~174^2~57 on 2012-08-27.

This change pre-dates Linux KASLR by 2 years. However, DebugFS may still be readable in some non-default configurations.

Procfs and Sysfs

The /proc and /sys pseudo-filesystems expose kernel addresses, memory layout details, symbol information, and hardware configuration. Many of these files are readable by unprivileged users by default.

The following KASLD components read from /proc:

The following KASLD components read from /sys:

Most of these are mitigated by kernel.kptr_restrict (for /proc/kallsyms, /proc/modules, etc.) and root-only permissions on sensitive sysfs entries.

Boot Configuration

Boot configuration and kernel config files can reveal whether KASLR is enabled, the PAGE_OFFSET (vmsplit), and other layout-relevant settings.

The following KASLD components read boot configuration:

  • boot-config.c — reads /boot/config-* for CONFIG_RELOCATABLE, CONFIG_RANDOMIZE_BASE, and CONFIG_PAGE_OFFSET
  • proc-config.c — reads /proc/config.gz for the same configuration options
  • proc-cmdline.c — reads /proc/cmdline to check for nokaslr

Side-Channels

There are a plethora of viable side-channel attacks which can be used to break KASLR, including microarchitectural timing attacks, transient execution attacks, and software side-channels that exploit timing variations in kernel algorithms and data structures.

The following table catalogues known side-channel KASLR attacks.

Attack Year Status References
KernelSnitch 2025 Implemented (experimental): kernelsnitch.c
Futex hash-table timing leaks mm_struct directmap address (not _stext). x86_64, unprivileged. Requires KASLD_EXPERIMENTAL=1 (~1–30 min runtime). Mitigated by CONFIG_FUTEX_PRIVATE_HASH (mainline ~v6.14+) which removes mm_struct from the private futex hash key.
KernelSnitch: Side-Channel Attacks on Kernel Data Structures (Maar et al., 2025) — NDSS 2025
lukasmaar/kernelsnitch
GhostWrite (CVE-2024-44067) 2024 T-Head XuanTie C910/C920 RISC-V only (2 CPU models); kernel ≥6.14 disables vector extension as mitigation. GhostWrite
RISCover: Differential CPU Fuzz Testing (Thomas et al., 2025)
cispa/GhostWrite, cispa/RISCover
SLAM 2024 Requires Intel LAM / AMD UAI (no mainstream kernel support yet); Spectre-based, needs specific gadgets. Leaky Address Masking: Exploiting Unmasked Spectre Gadgets with Noncanonical Address Translation (Hertogh et al., 2024)
vusec.net/projects/slam, vusec/slam
SLUBStick (CVE-2024-26808) 2024 Achieves arbitrary kernel read/write (enabling KASLR bypass) via allocator timing side-channel, but requires a pre-existing heap vulnerability (UAF, heap overflow). Not a standalone KASLR bypass. SLUBStick: Arbitrary Memory Writes through Practical Software Cross-Cache Attacks within the Linux Kernel (Maar et al., 2024) — USENIX Security 2024
Downfall (CVE-2022-40982) 2023 Mitigated by microcode on affected Intel CPUs (6th-11th gen); Gather Data Sampling, complex setup. Downfall: Exploiting Speculative Data Gathering (Moghimi, 2023)
Timing Transient Execution 2023 Depends on Meltdown-type transient execution; mitigated by KPTI on all affected Intel CPUs. Timing the Transient Execution: A New Side-Channel Attack on Intel CPUs (Jin et al., 2023)
AMD Prefetch Attacks (CVE-2021-26318) 2022 Mitigated on Zen 3+ via microcode (AMD-SB-1017); redundant on older AMD / VMs where prefetch.c also works. AMD Prefetch Attacks through Power and Time (Lipp et al., 2022) — USENIX Security 2022
AMD-SB-1017
amdprefetch/amd-prefetch-attacks
AMD RAPL power side-channel (CVE-2021-26318) 2022 Unprivileged RAPL access blocked since Linux 5.10; requires amd_energy module (not loaded by default); mitigated by same microcode as timing variant. AMD Prefetch Attacks through Power and Time (Lipp et al., 2022)
EntryBleed (CVE-2022-4543) 2022 Implemented: entrybleed.c
Intel x86_64 with KPTI enabled or disabled; AMD x86_64 with KPTI disabled. Requires kernel-version-specific offsets. Patched in kernel ~v6.2 (randomized per-CPU entry areas).
EntryBleed: Breaking KASLR under KPTI with Prefetch (CVE-2022-4543) (willsroot, 2022)
EntryBleed: A Universal KASLR Bypass against KPTI on Linux (William Liu, Joseph Ravichandran, Mengjia Yan, 2023)
RETBLEED 2022 Kernel mitigated (IBRS/eIBRS, retpoline); requires specific Intel (6th-8th gen) or AMD (Zen 1/1+/2) CPUs. RETBLEED: Arbitrary Speculative Code Execution with Return Instructions (Wikner & Razavi, 2022)
comsec-group/retbleed
SLS (CVE-2021-26341) 2022 AMD Zen 1/2 only; requires eBPF JIT (restricted since Linux 5.8); mitigated by INT3/LFENCE after every unconditional branch. The AMD Branch (Mis)predictor Part 2: Where No CPU has Gone Before (Wieczorkiewicz, 2022)
Straight-line Speculation Whitepaper (ARM, 2020)
ThermalBleed 2022 Thermal side-channel operates at ms-second timescale; far too slow/noisy for KASLR (needs sub-µs resolution). ThermalBleed: A Practical Thermal Side-Channel Attack (Kim & Shin, 2022)
Memory deduplication timing 2021 Requires KSM enabled (disabled by default on most distros); primarily a VM-to-VM attack. Memory deduplication as a threat to the guest OS (Suzaki et al., 2011)
Breaking KASLR Using Memory Deduplication in Virtualized Environments (Kim et al., 2021)
Remote Memory-Deduplication Attacks (Schwarzl et al., 2022)
VDSO sidechannel 2021 ARM64 only; requires custom kernel gadget in VDSO; mitigated by Spectre barriers in VDSO code. VDSO As A Potential KASLR Oracle (Pettersson & Radocea, 2021)
EchoLoad 2020 Implemented (experimental): echoload.c
Intel x86_64 only; relies on Meltdown zero-return behavior. Supports TSX, speculation, and signal-handler transient modes. No signal on non-vulnerable hardware (AMD, modern Intel with in-silicon Meltdown fix). Mitigated by KPTI on patched kernels. Requires KASLD_EXPERIMENTAL=1.
KASLR: Break It, Fix It, Repeat (Claudio Canella, Michael Schwarz, Martin Haubenwallner, 2020)
Store-to-Leak Forwarding: There and Back Again (Canella et al., 2020) — Slides, Blackhat Asia 2020
cc0x1f/store-to-leak-forwarding/echoload
PLATYPUS 2020 Unprivileged RAPL access restricted since Linux 5.10 (powercap driver); requires Intel CPU with specific RAPL interface. PLATYPUS: Software-based Power Side-Channel Attacks on x86 (Lipp et al., 2020)
TagBleed 2020 Requires Intel CPU with tagged TLBs and a VMM environment; narrow applicability. TagBleed: Breaking KASLR on the Isolated Kernel Address Space using Tagged TLBs (Koschel et al., 2020)
renorobert/tagbleedvmm
MDS / ZombieLoad / RIDL / Fallout 2019 Mitigated by microcode + kernel (MDS buffer clearing on context switch). Requires specific vulnerable Intel CPU generations (pre-Cascade Lake). Fallout: Leaking Data on Meltdown-resistant CPUs (Canella et al., 2019) — fallout_kaslr.c
RIDL: Rogue In-Flight Data Load (van Schaik et al., 2019) — vusec/ridl
ZombieLoadIAIK/ZombieLoad, zombieload_kaslr.c
Data Bounce 2019 Implemented (experimental): databounce.c
Intel x86_64 only; requires TSX (RTM). Exploits store-to-load forwarding within a TSX transaction. Works with KPTI enabled or disabled, bare metal and VMs. TSX deprecated by Intel, disabled via microcode on most consumer CPUs since 2019 (TAA mitigation). Requires KASLD_EXPERIMENTAL=1.
Store-to-Leak Forwarding: Leaking Data on Meltdown-resistant CPUs (Michael Schwarz, Claudio Canella, Lukas Giner, Daniel Gruss, 2019)
cc0x1f/store-to-leak-forwarding/data_bounce
Meltdown 2018 Fully mitigated by KPTI on all vulnerable CPUs; KPTI enabled by default since 2018. Meltdown: Reading Kernel Memory from User Space (Lipp et al., 2018) — USENIX Security 2018
IAIK/meltdown, paboldin/meltdown-exploit
Spectre v1 / v2 2018 Heavily mitigated (retpoline, IBRS/eIBRS, eBPF verifier hardening). KASLR break requires eBPF JIT or specific kernel gadgets; eBPF restricted to CAP_BPF since Linux 5.8. Spectre Attacks: Exploiting Speculative Execution (Kocher et al., 2018)
Reading privileged memory with a side-channel (Jann Horn, 2018)
speed47/spectre-meltdown-checker
SPECULOSE 2018 Equivalent to prefetch-style probing via speculative execution; fully mitigated by KPTI. SPECULOSE: Analyzing the Security Implications of Speculative Execution in CPUs (Maisuradze & Rossow, 2018)
Prefetch side-channel 2016 Implemented: prefetch.c
Intel and AMD x86_64. Requires KPTI to be disabled (kernel auto-disables KPTI on non-Meltdown-vulnerable CPUs: all AMD, Intel Ice Lake+). Does not require kernel-version-specific offsets. May fail silently on some newer AMD microarchitectures (Zen 3+) where the prefetch timing differential is absent.
Prefetch Side-Channel Attacks: Bypassing SMAP and Kernel ASLR (Daniel Gruss, Clémentine Maurice, Anders Fogh, 2016)
Using Undocumented CPU Behaviour to See into Kernel Mode and Break KASLR in the Process (Anders Fogh, Daniel Gruss, 2016) — Blackhat USA
xairy/kernel-exploits/prefetch-side-channel
Fetching the KASLR slide with prefetch (Seth Jenkins, 2022) — prefetch_poc.zip
BTB side-channel 2016 Complex implementation; largely superseded by simpler prefetch / EntryBleed techniques. Jump Over ASLR: Attacking Branch Predictors to Bypass ASLR (Evtyushkin et al., 2016)
felixwilhelm/mario_baslr
TSX/RTM abort timing (DrK) 2016 TSX deprecated by Intel, disabled via microcode on most consumer CPUs since 2019 (TAA mitigation). Redundant with Data Bounce on TSX-capable hardware. TSX improves timing attacks against KASLR (Rafal Wojtczuk, 2014)
DrK: Breaking KASLR with Intel TSX (Jang et al., 2016) — Blackhat USA
vnik5287/kaslr_tsx_bypass
Double page fault timing 2013 Precursor to prefetch side-channel; fully mitigated by KPTI (Meltdown patches). Superseded by prefetch / EntryBleed. Practical Timing Side Channel Attacks Against Kernel Space ASLR (Hund et al., 2013)
SIDT/SGDT IDT/GDT base leak 2004 Implemented: sidt.c
x86/x86_64. Unprivileged SIDT instruction reads IDT register containing kernel pointer. Only works on pre-3.10 kernels where idt_table was in kernel BSS. Mitigated by IDT-to-fixmap remapping (v3.10, 2013; predates KASLR v3.14), KPTI (v4.15, 2018), and UMIP hardware (Intel Cannon Lake+ / AMD Zen 2+). Never viable against vanilla KASLR kernels. Originally used for VM detection (Red Pill, 2004); later demonstrated as a KASLR bypass against out-of-tree patches (Hund, 2013).
KASLR is Dead: Long Live KASLR (Gruss et al., 2017) — Section 2 lists SIDT as a known KASLR bypass
Practical Timing Side Channel Attacks Against Kernel Space ASLR (Hund et al., 2013)
Red Pill (Joanna Rutkowska, 2004)

Note: Several related attacks (LVI, RAMBleed) are omitted from the table because they are not KASLR bypass techniques. LVI targets SGX enclaves; RAMBleed is a general memory read primitive (rowhammer-based, hours-slow).

The extra/check-hardware-vulnerabilities script performs rudimentary checks for several known hardware vulnerabilities, but does not implement these techniques.

See also:

Syscall and Interface Leaks

Kernel syscalls and device interfaces can leak kernel addresses through return values, uninitialized memory in structures, or sampling kernel events.

The following KASLD components exploit syscall and interface leaks:

  • perf_event_open.c — samples kernel event addresses via perf_event_open() (requires kernel.perf_event_paranoid < 2)
  • mincore.cmincore() heap page disclosure via uninitialized memory (CVE-2017-16994; patched in v4.15)
  • bcm_msg_head_struct.c — CAN BCM bcm_msg_head struct uninitialized 4-byte hole leaks kernel stack pointer (CVE-2021-34693)
  • pppd_kallsyms.c — exploits set-uid pppd to read /proc/kallsyms bypassing kptr_restrict open-time check
  • qemu-tcg-iret.c — leaks kernel stack address inside QEMU TCG guests via iret instruction (patched in QEMU 9.1)

Brute Force

Some memory layout properties can be determined by probing the address space directly, without reading any files or exploiting vulnerabilities.

The following KASLD components use brute-force probing:

  • mmap-brute-vmsplit.c — determines PAGE_OFFSET (vmsplit) on 32-bit systems by mapping pages across the address space until failure

Weak Entropy

The kernel is loaded at an aligned memory address, usually between PAGE_SIZE (4 KiB) and 2 MiB on modern systems (see KERNEL_ALIGN definitions in kasld.h). This limits the number of possible kernel locations. For example, on x86_64 with RANDOMIZE_BASE_MAX_OFFSET of 1 GiB and 2 MiB alignment, this limits the kernel load address to 0x4000_0000 / 0x20_0000 = 512 possible locations.

Weaknesses in randomisation can decrease entropy, further limiting the possible kernel locations in memory and making the kernel easier to locate.

KASLR may be disabled if insufficient randomness is generated during boot (for example, if get_kaslr_seed() fails on ARM64).

The following KASLD component provides a baseline reference:

  • default.c — reports the hardcoded default kernel text base address for the target architecture, used as the baseline for KASLR slide calculation

See also:

Patched Kernel Bugs

There have been many kernel bugs which leaked kernel addresses to unprivileged users via uninitialized memory, missing pointer sanitization, using kernel pointers as a basis for "random" strings in userland, and many other weird and wonderful flaws. These bugs are regularly discovered and patched.

Patched kernel info leak bugs:

Patched kernel info leak bugs caught by KernelMemorySanitizer (KMSAN):

Netfilter info leak (CVE-2022-1972):

Remote uninitialized stack variables leaked via Bluetooth:

Remote kernel pointer leak via IP packet headers (CVE-2019-10639):

floppy block driver show_floppy kernel function pointer leak (CVE-2018-7273) (requires floppy driver and access to dmesg).

kernel_waitid leak (CVE-2017-14954) (affects kernels 4.13-rc1 to 4.13.4):

snd_timer_user_read uninitialized kernel heap memory disclosure (CVE-2017-1000380):

Uninitialized kernel heap memory in ELF core dumps (CVE-2020-10732). fill_thread_core_info() in fs/binfmt_elf.c allocated regset data buffers with kmalloc(), which were not fully initialized by regset get() callbacks. Several kilobytes of stale kernel heap data (potentially containing kernel pointers) could be written to the core file and read by an unprivileged user. Trivially exploitable by crashing any program:

Uninitialized x86 FPU/xstate data in core dumps. copy_xstate_to_kernel() only copied enabled xstate features, leaving gaps between features uninitialized. Stale kernel memory was leaked through the NT_X86_XSTATE ELF core dump note:

RISC-V kernel gp register leaked to userland (CVE-2024-35871) (affects kernels 4.15 to 6.8.5). The kernel __global_pointer$ was exposed via childregs->gp in user_mode_helper threads (PID 1, core_pattern pipe handlers, etc), observable through kernel_execve register state, ptrace(PTRACE_GETREGSET), and PERF_SAMPLE_REGS_USER:

PPTP sockets pptp_bind() / pptp_connect() kernel stack leak (CVE-2015-8569):

Exploiting uninitialized stack variables:

Arbitrary Read

Kernel vulnerabilities which provide arbitrary read (or write) primitives can be leveraged to leak kernel pointers and defeat KASLR, even when direct info leak vectors are unavailable.

Leaking kernel addresses using msg_msg struct for arbitrary read (for KMALLOC_CGROUP objects):

Leaking kernel addresses using privileged arbitrary read (or write) in kernel space:

License

KASLD is MIT licensed but borrows heavily from modified third-party code snippets and proof of concept code.

Various code snippets were taken from third-parties and may have different license restrictions. Refer to the reference URLs in the comment headers available in each file for credits and more information.

About

A tool for inferring the Linux kernel base address and physical memory layout as an unprivileged local user, for the purpose of bypassing Kernel Address Space Layout Randomization (KASLR).

Topics

Resources

License

Stars

Watchers

Forks

Contributors