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)
- Usage
- Building
- Configuration
- Architecture and Design
- KASLR and Kernel Memory Layout
- KASLR Bypass Techniques
- License
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).
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
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
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.
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.
The orchestrator runs each component as an isolated child process:
fork()+execl()— the component runs in its own process group- stdout and stderr are merged into a single pipe back to the orchestrator
- The orchestrator reads lines from the pipe, capturing tagged lines as results and (in verbose mode) printing all output
- A per-component timeout (default: 30 seconds, configurable via
--timeout) kills the component and its children if it does not exit in time - 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.
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.
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.
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.
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.
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-xxxand4.15.0-xxxLTS kernels that contain backported fixes from mainline 5.x and 6.x. - RHEL ships
3.10.0-xxxkernels with fixes from mainline 4.x and 5.x. - A kernel reporting
6.8.0may 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.
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.
| 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 |
The label field identifies the source of the result. Convention:
- Simple label: the component name (
proc-kallsyms,sysfs_vmcoreinfo) - Qualified label:
component:qualifierwhen 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)
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:
- SUCCESS — component emitted at least one tagged line
- TIMEOUT — component was killed by the timeout
- ACCESS_DENIED — exit code 77
- UNAVAILABLE — exit code 69
- 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).
// 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");
#endifThe #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.
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 |
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 (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:
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:
- grsecurity - KASLR: An Exercise in Cargo Cult Security (grsecurity, 2013)
- An Info-Leak Resistant Kernel Randomization for Virtualized Systems | IEEE Journals & Magazine | IEEE Xplore (Fernando Vano-Garcia, Hector Marco-Gisbert, 2020)
- Kernel Address Space Layout Randomization (LWN.net)
- Kernel load address randomization · Linux Inside
- KASLR Kconfig options:
- CONFIG_RANDOMIZE_BASE: Randomize the address of the kernel image (KASLR)
- CONFIG_RANDOMIZE_BASE_MAX_OFFSET: Maximum kASLR offset
- CONFIG_RANDOMIZE_MEMORY: Randomize the kernel memory sections
- CONFIG_RANDOMIZE_MEMORY_PHYSICAL_PADDING: Physical memory mapping padding
- CONFIG_RELOCATABLE: Build a relocatable kernel
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.
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:
- security things in Linux v4.8 (Kees Cook, 2016) — describes x86_64 physical/virtual decoupling and
CONFIG_RANDOMIZE_MEMORY - x86, boot: KASLR memory randomization [LWN.net] (Thomas Garnier, 2016) —
CONFIG_RANDOMIZE_MEMORYpatch series - Kernel load address randomization · Linux Inside — detailed walkthrough of
choose_random_location()on x86
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.
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:
- 0xAX/linux-insides
- Virtual Memory and Linux (Matt Porter, 2016)
- Understanding the Linux Virtual Memory Manager (Mel Gorman, 2004)
- Linux Kernel Programming (Kaiwan N Billimoria, 2021)
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.
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.
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:
- dmesg_android_ion_snapshot.c
- dmesg_backtrace.c
- dmesg_check_for_initrd.c
- dmesg_cma_reserved.c
- dmesg_crashkernel.c
- dmesg_driver_component_ops.c
- dmesg_e820_memory_map.c
- dmesg_early_init_dt_add_memory_arch.c
- dmesg_efi_memmap.c
- dmesg_ex_handler_msr.c
- dmesg_fake_numa_init.c
- dmesg_free_area_init_node.c
- dmesg_free_reserved_area.c
- dmesg_kaslr-disabled.c
- dmesg_last_pfn.c
- dmesg_mem_init_kernel_layout.c
- dmesg_mmu_idmap.c
- dmesg_node_data.c
- dmesg_ramdisk.c
- dmesg_reserved_mem.c
- dmesg_reserved_mem_opensbi.c
- dmesg_riscv_relocation.c
- dmesg_swiotlb.c
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.
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.
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:
- proc-kallsyms.c — kernel symbol addresses from
/proc/kallsyms - proc-modules.c — loaded module addresses from
/proc/modules - proc-zoneinfo.c — memory zone boundaries from
/proc/zoneinfo - proc-cpuinfo.c — CPU information from
/proc/cpuinfo - proc-pid-syscall.c — kernel stack pointer from
/proc/<pid>/syscall - proc-stat-wchan.c — wait channel address from
/proc/<pid>/stat - proc-cmdline.c — kernel command line from
/proc/cmdline(checks fornokaslr) - proc-config.c — kernel configuration from
/proc/config.gz
The following KASLD components read from /sys:
- sysfs_firmware_memmap.c — firmware memory map from
/sys/firmware/memmap/ - sysfs_memory_blocks.c — memory block addresses from
/sys/devices/system/memory/ - sysfs_pci_resource.c — PCI BAR addresses from
/sys/bus/pci/devices/ - sysfs_vmcoreinfo.c — kernel addresses from
/sys/kernel/vmcoreinfo - sysfs_devicetree_initrd.c — initrd address from
/sys/firmware/devicetree/ - sysfs_devicetree_memory.c — memory regions from
/sys/firmware/devicetree/ - sysfs_iscsi_transport_handle.c — iSCSI transport handle from
/sys/class/iscsi_transport/ - sysfs-kernel-notes-xen.c — Xen notes from
/sys/kernel/notes - sysfs-module-sections.c — module section addresses from
/sys/module/*/sections/ - sysfs_nf_conntrack.c — netfilter conntrack hash from
/sys/module/nf_conntrack/
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 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-*forCONFIG_RELOCATABLE,CONFIG_RANDOMIZE_BASE, andCONFIG_PAGE_OFFSET - proc-config.c — reads
/proc/config.gzfor the same configuration options - proc-cmdline.c — reads
/proc/cmdlineto check fornokaslr
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 ZombieLoad — IAIK/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:
- google/safeside — project to understand and mitigate software-observable side-channels (Google)
- Hardening the Kernel Against Unprivileged Attacks (Claudio Canella, 2022)
- Exploiting Microarchitectural Optimizations from Software (Moritz Lipp. 2021)
- transient.fail — overview of speculative / transient execution attacks (Graz University of Technology, 2020)
- LVI: Hijacking Transient Execution through Microarchitectural Load Value Injection (Jo Van Bulck, Daniel Moghimi, Michael Schwarz, Moritz Lipp, Marina Minkin, Daniel Genkin, Yuval Yarom, Berk Sunar, Daniel Gruss, and Frank Piessens, 2020)
- A Systematic Evaluation of Transient Execution Attacks and Defenses (Claudio Canella, Jo Van Bulck, Michael Schwarz, Moritz Lipp, Benjamin von Berg, Philipp Ortner, Frank Piessens, Dmitry Evtyushkin, Daniel Gruss, 2019)
- RAMBleed: Reading Bits in Memory Without Accessing Them (Andrew Kwong, Daniel Genkin, Daniel Gruss, Yuval Yarom, 2019) — google/rowhammer-test
- Micro architecture attacks on KASLR (Anders Fogh, 2016)
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()(requireskernel.perf_event_paranoid < 2) - mincore.c —
mincore()heap page disclosure via uninitialized memory (CVE-2017-16994; patched in v4.15) - bcm_msg_head_struct.c — CAN BCM
bcm_msg_headstruct uninitialized 4-byte hole leaks kernel stack pointer (CVE-2021-34693) - pppd_kallsyms.c — exploits set-uid
pppdto read/proc/kallsymsbypassingkptr_restrictopen-time check - qemu-tcg-iret.c — leaks kernel stack address inside QEMU TCG guests via
iretinstruction (patched in QEMU 9.1)
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
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:
- Another look at two Linux KASLR patches (Kryptos Logic, 2020)
- arm64: efi: kaslr: Fix occasional random alloc (and boot) failure
- Defeating KASLR by Doing Nothing at All (Seth Jenkins, 2025) - arm64 linear map is not randomized due to memory hotplug support; Pixel bootloader loads kernel at static physical address, making kernel virtual addresses fully predictable even with KASLR enabled.
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:
- https://github.com/torvalds/linux/search?p=1&type=Commits&q=kernel-infoleak
git clone https://github.com/torvalds/linux && cd linux && git log | grep 'kernel-infoleak'
Patched kernel info leak bugs caught by KernelMemorySanitizer (KMSAN):
- https://github.com/torvalds/linux/search?p=1&type=Commits&q=BUG: KMSAN: kernel-infoleak
git clone https://github.com/torvalds/linux && cd linux && git log | grep "BUG: KMSAN: kernel-infoleak"
Netfilter info leak (CVE-2022-1972):
Remote uninitialized stack variables leaked via Bluetooth:
- BadChoice: Stack-Based Information Leak (BleedingTooth) (CVE-2020-12352)
- Linux Kernel: Infoleak in Bluetooth L2CAP Handling (CVE-2022-42895)
- Info Leak in the Linux Kernel via Bluetooth (CVE-2017-1000410)
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).
- Linux Kernel < 4.15.4 - 'show_floppy' KASLR Address Leak (Gregory Draperi. 2018)
- https://xorl.wordpress.com/2018/03/18/cve-2018-7273-linux-kernel-floppy-information-leak/
kernel_waitid leak (CVE-2017-14954) (affects kernels 4.13-rc1 to 4.13.4):
- wait_for_kaslr_to_be_effective.c (spender, 2017)
- https://github.com/salls/kernel-exploits/blob/master/CVE-2017-5123/exploit_no_smap.c (salls, 2017)
snd_timer_user_read uninitialized kernel heap memory disclosure (CVE-2017-1000380):
- Linux kernel 2.6.0 to 4.12-rc4 infoleak due to a data race in ALSA timer (Alexander Potapenko, 2017)
- snd_timer_c.bin (Alexander Potapenko, 2017)
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:
- google/kmsan#76: Uninitialized memory in ELF core dump
- Bug 1831399 - CVE-2020-10732 kernel: uninitialized kernel data leak in userspace coredumps
- fs/binfmt_elf.c: allocate initialized memory in fill_thread_core_info() (Alexander Potapenko, 2020)
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:
- Structure holes and information leaks (Jonathan Corbet. 2010)
- C Structure Padding Initialization (Noah Pendleton. 2022)
- DCL39-C. Avoid information leakage when passing a structure across a trust boundary - SEI CERT C Coding Standard
- Exploiting Uses of Uninitialized Stack Variables in Linux Kernels to Leak Kernel Pointers (Haehyun Cho, Jinbum Park, Joonwon Kang, Tiffany Bao, Ruoyu Wang, Yan Shoshitaishvili, Adam Doupé, Gail-Joon Ahn. 2020)
- Leak kernel pointer by exploiting uninitialized uses in Linux kernel
- jinb-park/leak-kptr
- compat_get_timex kernel stack pointer leak (CVE-2018-11508).
- sctp_af_inet kernel pointer leak (CVE-2017-7558) (requires
libsctp-dev). - rtnl_fill_link_ifmap kernel stack pointer leak (CVE-2016-4486).
- snd_timer_user_params kernel stack pointer leak (CVE-2016-4569).
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):
- Four Bytes of Power: Exploiting CVE-2021-26708 in the Linux kernel | Alexander Popov
- CVE-2021-22555: Turning \x00\x00 into 10000$ | security-research
- Exploiting CVE-2021-43267 - Haxxin
- Will's Root: pbctf 2021 Nightclub Writeup: More Fun with Linux Kernel Heap Notes!
- Will's Root: corCTF 2021 Fire of Salvation Writeup: Utilizing msg_msg Objects for Arbitrary Read and Arbitrary Write in the Linux Kernel
- [corCTF 2021] Wall Of Perdition: Utilizing msg_msg Objects For Arbitrary Read And Arbitrary Write In The Linux Kernel
- [CVE-2021-42008] Exploiting A 16-Year-Old Vulnerability In The Linux 6pack Driver
Leaking kernel addresses using privileged arbitrary read (or write) in kernel space:
- kptr_restrict – Finding kernel symbols for shell code (ryiron, 2013)
- CVE-2017-18344: Exploiting an arbitrary-read vulnerability in the Linux kernel timer subsystem (xairy, 2017):
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.
