KASLD's results can be checked, not just trusted. The core property to verify is that the range KASLD infers for the kernel image base contains the real value:
truth ∈ [virt_image_base_min, virt_image_base_max]
The same property holds on the physical axis — phys_truth ∈ [phys_kaslr_text_min, phys_kaslr_text_max] — on architectures that place the kernel image independently
in physical memory (x86-64, arm64, riscv64, s390); elsewhere the physical base is
a fixed projection of the virtual one and is covered by the check above.
A range that excludes the truth is a bug; a range that is wider than necessary is not (it just means less was recovered). There are four ways to verify this, from a boot-free structural check up to live-kernel soundness. All are runnable from a clean checkout.
These overlap in breadth — several run KASLD across many architectures — but each answers a different question, so they are complementary, not redundant:
| check | runs against | answers | cost |
|---|---|---|---|
tests/replay |
captured fixtures, offline | does KASLD parse and run cleanly over real kernel state? (structural — a regression / robustness check, no truth compared) | seconds |
tests/validate-fixtures |
the shipped truth-bearing captures, offline | does the inferred range contain the real base across many captured kernels, without a VM? | seconds |
extra/validate-bundle |
one captured bundle, offline | does the inferred range contain that system's truth? | seconds |
tests/vm/run |
a live kernel booted under qemu | does the inferred range contain a live kernel's truth, across arches and privilege levels? | minutes |
The key distinction is structural vs. soundness. tests/replay is structural
— it confirms the binary runs cleanly over many captured kernels but does not
compare against a truth. Soundness is checked directly: tests/validate-fixtures
does it offline over the captured kernels that carry ground truth — a real kallsyms
_text/_stext or an iomem "Kernel code" line — and tests/vm/run does it on a
freshly booted live kernel whose real base it knows directly. Many committed
captures keep that truth even though they are shared publicly: the anonymization
that prepares a fixture for sharing (extra/collect --anonymize, plus
extra/anonymize-fdt for device-tree boards) redacts host-identifying text —
hostname, CPU model, UUIDs, MACs, sensitive cmdline values — from
cpuinfo/cmdline/version/dmesg, but never touches /proc/kallsyms or the
iomem kernel line, so the base survives. The offline checks are cheap and
continuous; the VM check is slower and run periodically.
Capture the running system's state and confirm the inferred ranges contain the truth:
extra/collect --kallsyms # capture a self-contained bundle
extra/validate-bundle kasld-bundle-* # run kasld over it, check the truthcollect writes a path-preserving copy of the files KASLD reads plus the
kernel's real symbol addresses. validate-bundle runs the matching kasld binary
over that bundle offline and checks every inferred range against the captured
ground truth. It exits non-zero if any range excludes the truth. No root needed.
It also lists which vantage sources the bundle carries — the collecting process's identity, its MAC label, the active LSM list, the group database, and the container markers. Those do not affect an inferred range, so a missing one is reported rather than failed; it is reported at all because its absence is silent in the analysis, where an unread source and an empty answer both present as "unknown".
--kallsyms can only record the ground truth when kallsyms is readable
(kptr_restrict=0, or root). Without it the bundle carries no truth and the
checks report N/A rather than PASS — still not a failure, just nothing to
compare against.
tests/vm/run boots real, publicly-fetchable kernels under
qemu-system, runs KASLD against the running kernel, and applies the same check
across architectures and attacker profiles:
make cross # build the per-arch binaries
tests/vm/run # boot each supported arch, default profile
tests/vm/run all hardened # repeat under the unprivileged floorIt needs qemu-system-<arch> and the cross toolchains on PATH; an architecture
is skipped (not failed) when either is missing. The analysis always runs as an
unprivileged user (uid 1000) — KASLD's threat model is an unprivileged local
attacker, so every profile measures what such a user can leak, never what root
can. Every profile is a declared sysctl vector — never a "default-config"
assumption; the one privileged step is the per-boot ground-truth capture the
check compares against.
default is each kernel's own compile-time sysctl posture, read back at boot
and left as booted: kptr_restrict=0 and perf_event_paranoid=2 upstream, with
dmesg_restrict taking whatever the kernel .config sets (0 on the mainline
builds, 1 on Alpine). The other profiles move away from that booted baseline:
kptr-hidden, perf-open, dmesg-open and bpf-open each shift a single axis;
hardened tightens all of them at once.
default— the booted compile-time defaults.kptr_restrict=0alone does not expose symbol values: at the upstreamperf_event_paranoid=2,/proc/kallsymszeroes every address for an unprivileged reader, becausekallsyms_show_value()requiresperf_event_paranoid<=1orCAP_SYSLOG(generic across architectures). On a stock kernel the symbol table therefore yields nothing, and the base comes from sound inference alone.kptr-hidden—defaultpluskptr_restrict=2: kernel pointers are hidden. On a stock kernel this changes little overdefault— perf already gates kallsyms — so it isolates the effect ofkptr_restrictby itself.perf-open—defaultplusperf_event_paranoid=0. Dropping perf below 1 unlocks two independent sound signals at once:/proc/kallsymsnow shows real addresses, andperf_event_opentext-poke records leak a symbol pointer. Either pins the base exactly.dmesg-open—defaultplusdmesg_restrict=0: the world-readable ring buffer. This differs fromdefaultonly on kernels that shipdmesg_restrict=1(Alpine); on the mainline builds dmesg is already open.bpf-open—defaultplusunprivileged_bpf_disabled=0: unprivilegedbpf()permitted, which the BPF verifier-log leaks require. Theperf-openequivalent for the BPF axis. Cells fall into three groups. Where the kernel hasCONFIG_BPF_SYSCALLand unprivileged use is locked off — the distro cells shipCONFIG_BPF_UNPRIV_DEFAULT_OFF, the mainline ones are built with it forced on — that lock is what this profile relaxes, anddefaultreflects an attacker who cannot callbpf(). Roughly half the mainline cells do not setCONFIG_BPF_SYSCALLat all — among them everyx86_64,i686,armv7,armeb,ppc32and MIPS build — so there is nobpf()and nounprivileged_bpf_disabledsysctl to relax; the profile is a no-op there, and the boot log records the knob asabsent. On the 5.15 mainline cells that do haveCONFIG_BPF_SYSCALLthe forced lock did not take, so those boot with unprivilegedbpf()already permitted anddefaultmeasures the same vantagebpf-opendoes. The row is shown on every cell, like the other sysctl profiles, including where it reads the same asdefault— that is a result, not an absence, and it is the one this profile most often reports. Where the syscall exists, a row matchingdefaultsays unprivilegedbpf()was permitted and still leaked nothing; where it does not, the row says there was never a syscall to permit. A missing row would leave a reader unable to tell either case from a cell nobody ran. On a mainline cell that does havebpf(), the verifier-log components would still reportunavailable: their offset table is keyed byunameand built from a corpus of published distro kernels, which a locally built7.0.0is not in.hardened—kptr_restrict=2,dmesg_restrict=1,perf_event_paranoid=3,unprivileged_bpf_disabled=2: the realistic attacker floor, where only file-derived facts survive.
Under the tighter profiles the window may widen but must still contain the truth. See tests/vm/README.md for the full arch list and options.
Every architecture is booted on mainline kernel.org kernels — the LTS lines 5.15
and 6.6 and current 7.0 — cross-built from pinned source by tests/vm/build-kernel
(a stock upstream defconfig plus fixed config overlays: endianness, devtmpfs, and
text KASLR for riscv64 and ppc32). Of the fifteen, eight additionally boot a
publicly-fetchable Alpine distro kernel; the other seven — armeb, mips,
mipsel, mips64el, powerpc64, ppc32, riscv32 — have no Alpine port, so
mainline is their only source. Cells are named <arch>-<distro>-<line>;
for a mainline cell the version in the name is a label and LINUX_VERSION sets
the source:
LINUX_VERSION=5.15.211 tests/vm/build-kernel ppc32-mainline-5.15 # cross-build -> cache (slow)
tests/vm/run ppc32-mainline-5.15 # boot it, verdictarmeb needs -mbe8 on both payloads. The cross set's big-endian arm toolchain
emits BE32 by default, which cannot run on the BE8 userspace an arm kernel
provides from ARMv6 on — it takes SIGILL on its first instruction. -mbe8
produces BE8 instead, and make cross passes it for the armeb triple, as does
the VM harness for the init it stages beside kasld.
Which layout quantities KASLR randomizes — and therefore which columns the results tables below can carry at all — is a structural property of each architecture's memory model, not of whether the KASLR feature is enabled. Two architecture flags decide it: whether kernel text tracks the direct map (so a physical leak also fixes the virtual base), and whether the direct map is a static compile-time projection. Together they set, per architecture, whether the physical image base is an independently-randomized quantity and whether the direct-map / vmalloc / vmemmap region bases move on their own. This table is the reason a single flat grid does not fit: different architectures have different quantities to measure.
| architecture | text ↔ direct map | direct map | independent phys base | independent region bases |
|---|---|---|---|---|
| x86_64 | decoupled | randomized | yes | yes (RANDOMIZE_MEMORY) |
| aarch64 | decoupled | randomized | yes | no † |
| riscv64 | decoupled | randomized | yes | no † |
| s390x | decoupled | randomized | yes | no † |
| i686 | coupled | static | coupled | no |
| armv7 | coupled | static | coupled | no |
| mips, mipsel | coupled | static | coupled | no |
| mips64el | coupled | static | coupled | no |
| ppc32 | coupled | static | coupled | no |
| ppc64le, powerpc64 | coupled | static | coupled | no |
| riscv32 | coupled | static | coupled | no |
| loongarch64 | coupled | static | coupled | no |
Coupled means kernel text and the direct map share a fixed offset, so a
physical leak reveals the virtual base and there is no separate physical quantity
to score — the results matrix marks these coupled. Decoupled means the kernel
image is placed independently in physical memory, so the physical image base is
its own quantity. † The decoupled non-x86 architectures do randomize the linear
map, but by a mechanism (aarch64 memstart_addr, s390 identity base,
riscv64 kernel_map.page_offset) that this harness bounds but does not
ground-truth; only x86_64's CONFIG_RANDOMIZE_MEMORY region bases are truth-gated
(see Memory-KASLR region bases).
Reading the result cells in the tables below:
exact— the base was recovered (a byte-exact pin, or a window narrower than one KASLR slot); 0 bits of KASLR entropy remain.<n> bits— KASLR still randomized,nbits (2ⁿ candidate slots) unrecovered.—— KASLR is off on that cell, so there is no randomization to defeat.coupled— the quantity is a fixed projection of another (not independently randomized), so there is no separate value to score.n/a— the quantity does not exist on that architecture at all.
Soundness itself is not a per-cell column: every published cell is sound on every gated axis, and a cell whose window excludes the truth is withheld, never shown. The tables report how much was recovered; that the window contains the truth is the invariant behind the whole page.
This is the core soundness result — the virtual kernel-text base and, on
decoupled architectures, the physical image base. Every cell is sound: the true
base lies inside the guaranteed window on both axes, and a cell whose
window excludes the truth is withheld, never published. The snapshot is
generated by tests/vm/run all <scenario> followed by tests/vm/run table.
The guaranteed window is resolved purely from signals at or above the sound floor and never depends on a timing or microarchitectural side channel — that is what makes the soundness verdict reproducible run to run and machine to machine: every cell contains the truth on every boot, on any host.
The shape of the result, one row per architecture. tests/vm/run chart renders
it from the same rows as the table below, so the two cannot disagree:
A bar spans every kernel line and configuration tested rather than naming one number, because the spread is real: a different VA width or kernel line is a different window, not noise.
Reading a cell. source is the kernel: alpine (a distro kernel) or mainline
(a vanilla kernel.org build via tests/vm/build-kernel). virt residual and
phys residual say how much KASLR entropy KASLD could not strip from each
axis; the vocabulary (exact / <n> bits / — / coupled) is defined in
Architecture characterization above. phys residual carries an independent count only on the decoupled arches; elsewhere
it reads coupled, and the virtual result already determines it.
The bits are a floor, not a ceiling: they are the most KASLR entropy that survives sound, reproducible inference, and a microarchitectural oracle can strip more on capable hardware — see Speculative narrowing below. They are also one boot's sample rather than a constant: a row whose ceiling comes from a leak positioned relative to the kernel image has a span that tracks that boot's KASLR draw, so it can move a bit or two between runs, in either direction. Read a profile's effect from the component it unlocks, not from a small step.
Every profile is run on every cell and gets a row, including where it reads the
same as default — a profile that changed nothing is a result, and the table
would otherwise leave that indistinguishable from a cell nobody ran. Cells
carrying a configuration in the release column (7.0.0 (va39),
7.0.0 (vmsplit2g)) are purpose-built kernels on the config axis; see
Kernel-configuration sensitivity.
Two conventions explain rows that would otherwise look like gaps. An
architecture predating a kernel line has no row for it (LoongArch, mainlined in
6.1, has no 5.15 row). And a cell may show KASLR: off where its neighbours on
the same architecture show on: the three MIPS cells on the 6.6 line are built
without CONFIG_RANDOMIZE_BASE, because on that line MIPS Oopses in the
exception path once the kernel actually relocates. Those cells still test
soundness — an unprivileged process cannot read CONFIG_RANDOMIZE_BASE, so
KASLD reports the conservative KASLR-possible window, which must contain the
fixed base like any other.
The summary names the two scenarios that carry the result: default is the
ordinary unprivileged vantage, and perf-open is the one that moves the answer
on most architectures. The remaining scenarios — kptr-hidden, dmesg-open,
bpf-open, hardened, and the x86 paging modes — restate their cell's
default in all but 14 rows, and the fold beneath carries every one of them.
| arch | release | source | KASLR | default (virt / phys) | perf-open (virt / phys) |
|---|---|---|---|---|---|
| aarch64 | 6.12.81-0-virt | alpine | on | 31 bits / 14 bits | exact / 9 bits |
| aarch64 | 5.15.211 | mainline | on | 31 bits / 14 bits | exact / 9 bits |
| aarch64 | 6.6.144 | mainline | on | 31 bits / 14 bits | exact / 9 bits |
| aarch64 | 7.0.0 | mainline | on | 31 bits / 14 bits | exact / 9 bits |
| aarch64 | 7.0.0 (va39) | mainline | on | 22 bits / 14 bits | exact / 9 bits |
| aarch64 | 7.0.0 (va47) | mainline | on | 32 bits / 14 bits | exact / 9 bits |
| aarch64 | 7.0.0 (va42) | mainline | on | 24 bits / 13 bits | exact / 9 bits |
| aarch64 | 7.0.0 (va36) | mainline | on | 19 bits / 14 bits | exact / 9 bits |
| armeb | 5.15.211 | mainline | off | — / — | — / — |
| armeb | 6.6.144 | mainline | off | — / — | — / — |
| armeb | 7.0.0 | mainline | off | — / — | — / — |
| armv7 | 6.12.81-0-lts | alpine | off | — / — | — / — |
| armv7 | 5.15.211 | mainline | off | — / — | — / — |
| armv7 | 6.6.144 | mainline | off | — / — | — / — |
| armv7 | 7.0.0 | mainline | off | — / — | — / — |
| armv7 | 7.0.0 (vmsplit2g) | mainline | off | — / — | — / — |
| i686 | 6.12.81-0-lts | alpine | on | 5 bits / coupled | exact / coupled |
| i686 | 5.15.211 | mainline | on | 8 bits / coupled | exact / coupled |
| i686 | 6.6.144 | mainline | on | 8 bits / coupled | exact / coupled |
| i686 | 7.0.0 | mainline | on | 8 bits / coupled | exact / coupled |
| i686 | 7.0.0 (vmsplit2g) | mainline | on | 8 bits / coupled | exact / coupled |
| loongarch64 | 6.18.44-0-lts | alpine | on | 10 bits / coupled | exact / coupled |
| loongarch64 | 6.6.144 | mainline | on | 11 bits / coupled | exact / coupled |
| loongarch64 | 7.0.0 | mainline | on | 14 bits / coupled | exact / coupled |
| mips | 5.15.211 | mainline | on | 8 bits / coupled | 8 bits / coupled |
| mips | 6.6.144 | mainline | off | — / — | — / — |
| mips | 7.0.0 | mainline | on | 11 bits / coupled | 11 bits / coupled |
| mips64el | 5.15.211 | mainline | on | 8 bits / coupled | 8 bits / coupled |
| mips64el | 6.6.144 | mainline | off | — / — | — / — |
| mips64el | 7.0.0 | mainline | on | 14 bits / coupled | 14 bits / coupled |
| mipsel | 5.15.211 | mainline | on | 8 bits / coupled | 8 bits / coupled |
| mipsel | 6.6.144 | mainline | off | — / — | — / — |
| mipsel | 7.0.0 | mainline | on | 11 bits / coupled | 11 bits / coupled |
| powerpc64 | 5.15.211 | mainline | off | — / — | — / — |
| powerpc64 | 6.6.144 | mainline | off | — / — | — / — |
| powerpc64 | 7.0.0 | mainline | off | — / — | — / — |
| ppc32 | 5.15.211 | mainline | on | 13 bits / coupled | exact / coupled |
| ppc32 | 6.6.144 | mainline | on | 14 bits / coupled | exact / coupled |
| ppc32 | 7.0.0 | mainline | on | 14 bits / coupled | exact / coupled |
| ppc64le | 6.12.81-0-lts | alpine | off | — / — | — / — |
| ppc64le | 5.15.211 | mainline | off | — / — | — / — |
| ppc64le | 6.6.144 | mainline | off | — / — | — / — |
| ppc64le | 7.0.0 | mainline | off | — / — | — / — |
| riscv32 | 5.15.211 | mainline | off | — / — | — / — |
| riscv32 | 6.6.144 | mainline | off | — / — | — / — |
| riscv32 | 7.0.0 | mainline | off | — / — | — / — |
| riscv64 | 6.18.44-0-lts | alpine | off | — / — | — / — |
| riscv64 | 5.15.211 | mainline | off | — / — | — / — |
| riscv64 | 6.6.144 | mainline | on | exact / 9 bits | exact / 9 bits |
| riscv64 | 7.0.0 | mainline | on | 16 bits / 9 bits | exact / 9 bits |
| s390x | 6.12.81-0-lts | alpine | on | 39 bits / 10 bits | exact / 10 bits |
| s390x | 5.15.211 | mainline | on | 17 bits / 10 bits | exact / exact |
| s390x | 6.6.144 | mainline | on | 17 bits / 10 bits | exact / exact |
| s390x | 7.0.0 | mainline | on | 39 bits / 10 bits | exact / 10 bits |
| x86_64 | 6.12.81-0-virt | alpine | on | 2 bits / 6 bits | exact / 6 bits |
| x86_64 | 5.15.211 | mainline | on | 5 bits / 9 bits | exact / 9 bits |
| x86_64 | 6.6.144 | mainline | on | 5 bits / 9 bits | exact / 9 bits |
| x86_64 | 7.0.0 | mainline | on | 9 bits / 9 bits | exact / 9 bits |
Full results matrix — every scenario, 364 rows
| arch | release | source | scenario | KASLR | virt residual | phys residual |
|---|---|---|---|---|---|---|
| aarch64 | 6.12.81-0-virt | alpine | default | on | 31 bits | 14 bits |
| aarch64 | 6.12.81-0-virt | alpine | kptr-hidden | on | 31 bits | 14 bits |
| aarch64 | 6.12.81-0-virt | alpine | perf-open | on | exact | 9 bits |
| aarch64 | 6.12.81-0-virt | alpine | dmesg-open | on | 31 bits | 14 bits |
| aarch64 | 6.12.81-0-virt | alpine | bpf-open | on | 15 bits | 14 bits |
| aarch64 | 6.12.81-0-virt | alpine | hardened | on | 31 bits | 14 bits |
| aarch64 | 5.15.211 | mainline | default | on | 31 bits | 14 bits |
| aarch64 | 5.15.211 | mainline | kptr-hidden | on | 31 bits | 14 bits |
| aarch64 | 5.15.211 | mainline | perf-open | on | exact | 9 bits |
| aarch64 | 5.15.211 | mainline | dmesg-open | on | 31 bits | 14 bits |
| aarch64 | 5.15.211 | mainline | bpf-open | on | 31 bits | 14 bits |
| aarch64 | 5.15.211 | mainline | hardened | on | 31 bits | 14 bits |
| aarch64 | 6.6.144 | mainline | default | on | 31 bits | 14 bits |
| aarch64 | 6.6.144 | mainline | kptr-hidden | on | 31 bits | 14 bits |
| aarch64 | 6.6.144 | mainline | perf-open | on | exact | 9 bits |
| aarch64 | 6.6.144 | mainline | dmesg-open | on | 31 bits | 14 bits |
| aarch64 | 6.6.144 | mainline | bpf-open | on | 31 bits | 14 bits |
| aarch64 | 6.6.144 | mainline | hardened | on | 31 bits | 14 bits |
| aarch64 | 7.0.0 | mainline | default | on | 31 bits | 14 bits |
| aarch64 | 7.0.0 | mainline | kptr-hidden | on | 31 bits | 14 bits |
| aarch64 | 7.0.0 | mainline | perf-open | on | exact | 9 bits |
| aarch64 | 7.0.0 | mainline | dmesg-open | on | 31 bits | 14 bits |
| aarch64 | 7.0.0 | mainline | bpf-open | on | 31 bits | 14 bits |
| aarch64 | 7.0.0 | mainline | hardened | on | 31 bits | 14 bits |
| aarch64 | 7.0.0 (va39) | mainline | default | on | 22 bits | 14 bits |
| aarch64 | 7.0.0 (va39) | mainline | kptr-hidden | on | 22 bits | 14 bits |
| aarch64 | 7.0.0 (va39) | mainline | perf-open | on | exact | 9 bits |
| aarch64 | 7.0.0 (va39) | mainline | dmesg-open | on | 22 bits | 14 bits |
| aarch64 | 7.0.0 (va39) | mainline | bpf-open | on | 22 bits | 14 bits |
| aarch64 | 7.0.0 (va39) | mainline | hardened | on | 22 bits | 14 bits |
| aarch64 | 7.0.0 (va47) | mainline | default | on | 32 bits | 14 bits |
| aarch64 | 7.0.0 (va47) | mainline | kptr-hidden | on | 32 bits | 14 bits |
| aarch64 | 7.0.0 (va47) | mainline | perf-open | on | exact | 9 bits |
| aarch64 | 7.0.0 (va47) | mainline | dmesg-open | on | 32 bits | 14 bits |
| aarch64 | 7.0.0 (va47) | mainline | bpf-open | on | 32 bits | 14 bits |
| aarch64 | 7.0.0 (va47) | mainline | hardened | on | 32 bits | 14 bits |
| aarch64 | 7.0.0 (va42) | mainline | default | on | 24 bits | 13 bits |
| aarch64 | 7.0.0 (va42) | mainline | kptr-hidden | on | 24 bits | 13 bits |
| aarch64 | 7.0.0 (va42) | mainline | perf-open | on | exact | 9 bits |
| aarch64 | 7.0.0 (va42) | mainline | dmesg-open | on | 24 bits | 13 bits |
| aarch64 | 7.0.0 (va42) | mainline | bpf-open | on | 24 bits | 13 bits |
| aarch64 | 7.0.0 (va42) | mainline | hardened | on | 24 bits | 13 bits |
| aarch64 | 7.0.0 (va36) | mainline | default | on | 19 bits | 14 bits |
| aarch64 | 7.0.0 (va36) | mainline | kptr-hidden | on | 19 bits | 14 bits |
| aarch64 | 7.0.0 (va36) | mainline | perf-open | on | exact | 9 bits |
| aarch64 | 7.0.0 (va36) | mainline | dmesg-open | on | 19 bits | 14 bits |
| aarch64 | 7.0.0 (va36) | mainline | bpf-open | on | 19 bits | 14 bits |
| aarch64 | 7.0.0 (va36) | mainline | hardened | on | 19 bits | 14 bits |
| armeb | 5.15.211 | mainline | default | off | — | — |
| armeb | 5.15.211 | mainline | kptr-hidden | off | — | — |
| armeb | 5.15.211 | mainline | perf-open | off | — | — |
| armeb | 5.15.211 | mainline | dmesg-open | off | — | — |
| armeb | 5.15.211 | mainline | bpf-open | off | — | — |
| armeb | 5.15.211 | mainline | hardened | off | — | — |
| armeb | 6.6.144 | mainline | default | off | — | — |
| armeb | 6.6.144 | mainline | kptr-hidden | off | — | — |
| armeb | 6.6.144 | mainline | perf-open | off | — | — |
| armeb | 6.6.144 | mainline | dmesg-open | off | — | — |
| armeb | 6.6.144 | mainline | bpf-open | off | — | — |
| armeb | 6.6.144 | mainline | hardened | off | — | — |
| armeb | 7.0.0 | mainline | default | off | — | — |
| armeb | 7.0.0 | mainline | kptr-hidden | off | — | — |
| armeb | 7.0.0 | mainline | perf-open | off | — | — |
| armeb | 7.0.0 | mainline | dmesg-open | off | — | — |
| armeb | 7.0.0 | mainline | bpf-open | off | — | — |
| armeb | 7.0.0 | mainline | hardened | off | — | — |
| armv7 | 6.12.81-0-lts | alpine | default | off | — | — |
| armv7 | 6.12.81-0-lts | alpine | kptr-hidden | off | — | — |
| armv7 | 6.12.81-0-lts | alpine | perf-open | off | — | — |
| armv7 | 6.12.81-0-lts | alpine | dmesg-open | off | — | — |
| armv7 | 6.12.81-0-lts | alpine | bpf-open | off | — | — |
| armv7 | 6.12.81-0-lts | alpine | hardened | off | — | — |
| armv7 | 5.15.211 | mainline | default | off | — | — |
| armv7 | 5.15.211 | mainline | kptr-hidden | off | — | — |
| armv7 | 5.15.211 | mainline | perf-open | off | — | — |
| armv7 | 5.15.211 | mainline | dmesg-open | off | — | — |
| armv7 | 5.15.211 | mainline | bpf-open | off | — | — |
| armv7 | 5.15.211 | mainline | hardened | off | — | — |
| armv7 | 6.6.144 | mainline | default | off | — | — |
| armv7 | 6.6.144 | mainline | kptr-hidden | off | — | — |
| armv7 | 6.6.144 | mainline | perf-open | off | — | — |
| armv7 | 6.6.144 | mainline | dmesg-open | off | — | — |
| armv7 | 6.6.144 | mainline | bpf-open | off | — | — |
| armv7 | 6.6.144 | mainline | hardened | off | — | — |
| armv7 | 7.0.0 | mainline | default | off | — | — |
| armv7 | 7.0.0 | mainline | kptr-hidden | off | — | — |
| armv7 | 7.0.0 | mainline | perf-open | off | — | — |
| armv7 | 7.0.0 | mainline | dmesg-open | off | — | — |
| armv7 | 7.0.0 | mainline | bpf-open | off | — | — |
| armv7 | 7.0.0 | mainline | hardened | off | — | — |
| armv7 | 7.0.0 (vmsplit2g) | mainline | default | off | — | — |
| armv7 | 7.0.0 (vmsplit2g) | mainline | kptr-hidden | off | — | — |
| armv7 | 7.0.0 (vmsplit2g) | mainline | perf-open | off | — | — |
| armv7 | 7.0.0 (vmsplit2g) | mainline | dmesg-open | off | — | — |
| armv7 | 7.0.0 (vmsplit2g) | mainline | bpf-open | off | — | — |
| armv7 | 7.0.0 (vmsplit2g) | mainline | hardened | off | — | — |
| i686 | 6.12.81-0-lts | alpine | default | on | 5 bits | coupled |
| i686 | 6.12.81-0-lts | alpine | kptr-hidden | on | 5 bits | coupled |
| i686 | 6.12.81-0-lts | alpine | perf-open | on | exact | coupled |
| i686 | 6.12.81-0-lts | alpine | dmesg-open | on | 5 bits | coupled |
| i686 | 6.12.81-0-lts | alpine | bpf-open | on | 5 bits | coupled |
| i686 | 6.12.81-0-lts | alpine | hardened | on | 5 bits | coupled |
| i686 | 5.15.211 | mainline | default | on | 8 bits | coupled |
| i686 | 5.15.211 | mainline | kptr-hidden | on | 8 bits | coupled |
| i686 | 5.15.211 | mainline | perf-open | on | exact | coupled |
| i686 | 5.15.211 | mainline | dmesg-open | on | 8 bits | coupled |
| i686 | 5.15.211 | mainline | bpf-open | on | 8 bits | coupled |
| i686 | 5.15.211 | mainline | hardened | on | 8 bits | coupled |
| i686 | 6.6.144 | mainline | default | on | 8 bits | coupled |
| i686 | 6.6.144 | mainline | kptr-hidden | on | 8 bits | coupled |
| i686 | 6.6.144 | mainline | perf-open | on | exact | coupled |
| i686 | 6.6.144 | mainline | dmesg-open | on | 8 bits | coupled |
| i686 | 6.6.144 | mainline | bpf-open | on | 8 bits | coupled |
| i686 | 6.6.144 | mainline | hardened | on | 8 bits | coupled |
| i686 | 7.0.0 | mainline | default | on | 8 bits | coupled |
| i686 | 7.0.0 | mainline | kptr-hidden | on | 8 bits | coupled |
| i686 | 7.0.0 | mainline | perf-open | on | exact | coupled |
| i686 | 7.0.0 | mainline | dmesg-open | on | 8 bits | coupled |
| i686 | 7.0.0 | mainline | bpf-open | on | 8 bits | coupled |
| i686 | 7.0.0 | mainline | hardened | on | 8 bits | coupled |
| i686 | 7.0.0 (vmsplit2g) | mainline | default | on | 8 bits | coupled |
| i686 | 7.0.0 (vmsplit2g) | mainline | kptr-hidden | on | 8 bits | coupled |
| i686 | 7.0.0 (vmsplit2g) | mainline | perf-open | on | exact | coupled |
| i686 | 7.0.0 (vmsplit2g) | mainline | dmesg-open | on | 8 bits | coupled |
| i686 | 7.0.0 (vmsplit2g) | mainline | bpf-open | on | 8 bits | coupled |
| i686 | 7.0.0 (vmsplit2g) | mainline | hardened | on | 8 bits | coupled |
| loongarch64 | 6.18.44-0-lts | alpine | default | on | 10 bits | coupled |
| loongarch64 | 6.18.44-0-lts | alpine | kptr-hidden | on | 10 bits | coupled |
| loongarch64 | 6.18.44-0-lts | alpine | perf-open | on | exact | coupled |
| loongarch64 | 6.18.44-0-lts | alpine | dmesg-open | on | 11 bits | coupled |
| loongarch64 | 6.18.44-0-lts | alpine | bpf-open | on | 10 bits | coupled |
| loongarch64 | 6.18.44-0-lts | alpine | hardened | on | 10 bits | coupled |
| loongarch64 | 6.6.144 | mainline | default | on | 11 bits | coupled |
| loongarch64 | 6.6.144 | mainline | kptr-hidden | on | 10 bits | coupled |
| loongarch64 | 6.6.144 | mainline | perf-open | on | exact | coupled |
| loongarch64 | 6.6.144 | mainline | dmesg-open | on | 11 bits | coupled |
| loongarch64 | 6.6.144 | mainline | bpf-open | on | 11 bits | coupled |
| loongarch64 | 6.6.144 | mainline | hardened | on | 11 bits | coupled |
| loongarch64 | 7.0.0 | mainline | default | on | 14 bits | coupled |
| loongarch64 | 7.0.0 | mainline | kptr-hidden | on | 14 bits | coupled |
| loongarch64 | 7.0.0 | mainline | perf-open | on | exact | coupled |
| loongarch64 | 7.0.0 | mainline | dmesg-open | on | 14 bits | coupled |
| loongarch64 | 7.0.0 | mainline | bpf-open | on | 14 bits | coupled |
| loongarch64 | 7.0.0 | mainline | hardened | on | 14 bits | coupled |
| mips | 5.15.211 | mainline | default | on | 8 bits | coupled |
| mips | 5.15.211 | mainline | kptr-hidden | on | 8 bits | coupled |
| mips | 5.15.211 | mainline | perf-open | on | 8 bits | coupled |
| mips | 5.15.211 | mainline | dmesg-open | on | 8 bits | coupled |
| mips | 5.15.211 | mainline | bpf-open | on | 8 bits | coupled |
| mips | 5.15.211 | mainline | hardened | on | 8 bits | coupled |
| mips | 6.6.144 | mainline | default | off | — | — |
| mips | 6.6.144 | mainline | kptr-hidden | off | — | — |
| mips | 6.6.144 | mainline | perf-open | off | — | — |
| mips | 6.6.144 | mainline | dmesg-open | off | — | — |
| mips | 6.6.144 | mainline | bpf-open | off | — | — |
| mips | 6.6.144 | mainline | hardened | off | — | — |
| mips | 7.0.0 | mainline | default | on | 11 bits | coupled |
| mips | 7.0.0 | mainline | kptr-hidden | on | 11 bits | coupled |
| mips | 7.0.0 | mainline | perf-open | on | 11 bits | coupled |
| mips | 7.0.0 | mainline | dmesg-open | on | 11 bits | coupled |
| mips | 7.0.0 | mainline | bpf-open | on | 11 bits | coupled |
| mips | 7.0.0 | mainline | hardened | on | 11 bits | coupled |
| mips64el | 5.15.211 | mainline | default | on | 8 bits | coupled |
| mips64el | 5.15.211 | mainline | kptr-hidden | on | 8 bits | coupled |
| mips64el | 5.15.211 | mainline | perf-open | on | 8 bits | coupled |
| mips64el | 5.15.211 | mainline | dmesg-open | on | 8 bits | coupled |
| mips64el | 5.15.211 | mainline | bpf-open | on | 8 bits | coupled |
| mips64el | 5.15.211 | mainline | hardened | on | 8 bits | coupled |
| mips64el | 6.6.144 | mainline | default | off | — | — |
| mips64el | 6.6.144 | mainline | kptr-hidden | off | — | — |
| mips64el | 6.6.144 | mainline | perf-open | off | — | — |
| mips64el | 6.6.144 | mainline | dmesg-open | off | — | — |
| mips64el | 6.6.144 | mainline | bpf-open | off | — | — |
| mips64el | 6.6.144 | mainline | hardened | off | — | — |
| mips64el | 7.0.0 | mainline | default | on | 14 bits | coupled |
| mips64el | 7.0.0 | mainline | kptr-hidden | on | 14 bits | coupled |
| mips64el | 7.0.0 | mainline | perf-open | on | 14 bits | coupled |
| mips64el | 7.0.0 | mainline | dmesg-open | on | 14 bits | coupled |
| mips64el | 7.0.0 | mainline | bpf-open | on | 14 bits | coupled |
| mips64el | 7.0.0 | mainline | hardened | on | 14 bits | coupled |
| mipsel | 5.15.211 | mainline | default | on | 8 bits | coupled |
| mipsel | 5.15.211 | mainline | kptr-hidden | on | 8 bits | coupled |
| mipsel | 5.15.211 | mainline | perf-open | on | 8 bits | coupled |
| mipsel | 5.15.211 | mainline | dmesg-open | on | 8 bits | coupled |
| mipsel | 5.15.211 | mainline | bpf-open | on | 8 bits | coupled |
| mipsel | 5.15.211 | mainline | hardened | on | 8 bits | coupled |
| mipsel | 6.6.144 | mainline | default | off | — | — |
| mipsel | 6.6.144 | mainline | kptr-hidden | off | — | — |
| mipsel | 6.6.144 | mainline | perf-open | off | — | — |
| mipsel | 6.6.144 | mainline | dmesg-open | off | — | — |
| mipsel | 6.6.144 | mainline | bpf-open | off | — | — |
| mipsel | 6.6.144 | mainline | hardened | off | — | — |
| mipsel | 7.0.0 | mainline | default | on | 11 bits | coupled |
| mipsel | 7.0.0 | mainline | kptr-hidden | on | 11 bits | coupled |
| mipsel | 7.0.0 | mainline | perf-open | on | 11 bits | coupled |
| mipsel | 7.0.0 | mainline | dmesg-open | on | 11 bits | coupled |
| mipsel | 7.0.0 | mainline | bpf-open | on | 11 bits | coupled |
| mipsel | 7.0.0 | mainline | hardened | on | 11 bits | coupled |
| powerpc64 | 5.15.211 | mainline | default | off | — | — |
| powerpc64 | 5.15.211 | mainline | kptr-hidden | off | — | — |
| powerpc64 | 5.15.211 | mainline | perf-open | off | — | — |
| powerpc64 | 5.15.211 | mainline | dmesg-open | off | — | — |
| powerpc64 | 5.15.211 | mainline | bpf-open | off | — | — |
| powerpc64 | 5.15.211 | mainline | hardened | off | — | — |
| powerpc64 | 6.6.144 | mainline | default | off | — | — |
| powerpc64 | 6.6.144 | mainline | kptr-hidden | off | — | — |
| powerpc64 | 6.6.144 | mainline | perf-open | off | — | — |
| powerpc64 | 6.6.144 | mainline | dmesg-open | off | — | — |
| powerpc64 | 6.6.144 | mainline | bpf-open | off | — | — |
| powerpc64 | 6.6.144 | mainline | hardened | off | — | — |
| powerpc64 | 7.0.0 | mainline | default | off | — | — |
| powerpc64 | 7.0.0 | mainline | kptr-hidden | off | — | — |
| powerpc64 | 7.0.0 | mainline | perf-open | off | — | — |
| powerpc64 | 7.0.0 | mainline | dmesg-open | off | — | — |
| powerpc64 | 7.0.0 | mainline | bpf-open | off | — | — |
| powerpc64 | 7.0.0 | mainline | hardened | off | — | — |
| ppc32 | 5.15.211 | mainline | default | on | 13 bits | coupled |
| ppc32 | 5.15.211 | mainline | kptr-hidden | on | 14 bits | coupled |
| ppc32 | 5.15.211 | mainline | perf-open | on | exact | coupled |
| ppc32 | 5.15.211 | mainline | dmesg-open | on | 14 bits | coupled |
| ppc32 | 5.15.211 | mainline | bpf-open | on | 14 bits | coupled |
| ppc32 | 5.15.211 | mainline | hardened | on | 14 bits | coupled |
| ppc32 | 6.6.144 | mainline | default | on | 14 bits | coupled |
| ppc32 | 6.6.144 | mainline | kptr-hidden | on | 14 bits | coupled |
| ppc32 | 6.6.144 | mainline | perf-open | on | exact | coupled |
| ppc32 | 6.6.144 | mainline | dmesg-open | on | 14 bits | coupled |
| ppc32 | 6.6.144 | mainline | bpf-open | on | 13 bits | coupled |
| ppc32 | 6.6.144 | mainline | hardened | on | 14 bits | coupled |
| ppc32 | 7.0.0 | mainline | default | on | 14 bits | coupled |
| ppc32 | 7.0.0 | mainline | kptr-hidden | on | 14 bits | coupled |
| ppc32 | 7.0.0 | mainline | perf-open | on | exact | coupled |
| ppc32 | 7.0.0 | mainline | dmesg-open | on | 11 bits | coupled |
| ppc32 | 7.0.0 | mainline | bpf-open | on | 14 bits | coupled |
| ppc32 | 7.0.0 | mainline | hardened | on | 14 bits | coupled |
| ppc64le | 6.12.81-0-lts | alpine | default | off | — | — |
| ppc64le | 6.12.81-0-lts | alpine | kptr-hidden | off | — | — |
| ppc64le | 6.12.81-0-lts | alpine | perf-open | off | — | — |
| ppc64le | 6.12.81-0-lts | alpine | dmesg-open | off | — | — |
| ppc64le | 6.12.81-0-lts | alpine | bpf-open | off | — | — |
| ppc64le | 6.12.81-0-lts | alpine | hardened | off | — | — |
| ppc64le | 5.15.211 | mainline | default | off | — | — |
| ppc64le | 5.15.211 | mainline | kptr-hidden | off | — | — |
| ppc64le | 5.15.211 | mainline | perf-open | off | — | — |
| ppc64le | 5.15.211 | mainline | dmesg-open | off | — | — |
| ppc64le | 5.15.211 | mainline | bpf-open | off | — | — |
| ppc64le | 5.15.211 | mainline | hardened | off | — | — |
| ppc64le | 6.6.144 | mainline | default | off | — | — |
| ppc64le | 6.6.144 | mainline | kptr-hidden | off | — | — |
| ppc64le | 6.6.144 | mainline | perf-open | off | — | — |
| ppc64le | 6.6.144 | mainline | dmesg-open | off | — | — |
| ppc64le | 6.6.144 | mainline | bpf-open | off | — | — |
| ppc64le | 6.6.144 | mainline | hardened | off | — | — |
| ppc64le | 7.0.0 | mainline | default | off | — | — |
| ppc64le | 7.0.0 | mainline | kptr-hidden | off | — | — |
| ppc64le | 7.0.0 | mainline | perf-open | off | — | — |
| ppc64le | 7.0.0 | mainline | dmesg-open | off | — | — |
| ppc64le | 7.0.0 | mainline | bpf-open | off | — | — |
| ppc64le | 7.0.0 | mainline | hardened | off | — | — |
| riscv32 | 5.15.211 | mainline | default | off | — | — |
| riscv32 | 5.15.211 | mainline | kptr-hidden | off | — | — |
| riscv32 | 5.15.211 | mainline | perf-open | off | — | — |
| riscv32 | 5.15.211 | mainline | dmesg-open | off | — | — |
| riscv32 | 5.15.211 | mainline | bpf-open | off | — | — |
| riscv32 | 5.15.211 | mainline | hardened | off | — | — |
| riscv32 | 6.6.144 | mainline | default | off | — | — |
| riscv32 | 6.6.144 | mainline | kptr-hidden | off | — | — |
| riscv32 | 6.6.144 | mainline | perf-open | off | — | — |
| riscv32 | 6.6.144 | mainline | dmesg-open | off | — | — |
| riscv32 | 6.6.144 | mainline | bpf-open | off | — | — |
| riscv32 | 6.6.144 | mainline | hardened | off | — | — |
| riscv32 | 7.0.0 | mainline | default | off | — | — |
| riscv32 | 7.0.0 | mainline | kptr-hidden | off | — | — |
| riscv32 | 7.0.0 | mainline | perf-open | off | — | — |
| riscv32 | 7.0.0 | mainline | dmesg-open | off | — | — |
| riscv32 | 7.0.0 | mainline | bpf-open | off | — | — |
| riscv32 | 7.0.0 | mainline | hardened | off | — | — |
| riscv64 | 6.18.44-0-lts | alpine | default | off | — | — |
| riscv64 | 6.18.44-0-lts | alpine | kptr-hidden | off | — | — |
| riscv64 | 6.18.44-0-lts | alpine | perf-open | off | — | — |
| riscv64 | 6.18.44-0-lts | alpine | dmesg-open | off | — | — |
| riscv64 | 6.18.44-0-lts | alpine | bpf-open | off | — | — |
| riscv64 | 6.18.44-0-lts | alpine | hardened | off | — | — |
| riscv64 | 6.18.44-0-lts | alpine | no5lvl | off | — | — |
| riscv64 | 6.18.44-0-lts | alpine | no4lvl | off | — | — |
| riscv64 | 5.15.211 | mainline | default | off | — | — |
| riscv64 | 5.15.211 | mainline | kptr-hidden | off | — | — |
| riscv64 | 5.15.211 | mainline | perf-open | off | — | — |
| riscv64 | 5.15.211 | mainline | dmesg-open | off | — | — |
| riscv64 | 5.15.211 | mainline | bpf-open | off | — | — |
| riscv64 | 5.15.211 | mainline | hardened | off | — | — |
| riscv64 | 5.15.211 | mainline | no5lvl | off | — | — |
| riscv64 | 5.15.211 | mainline | no4lvl | off | — | — |
| riscv64 | 6.6.144 | mainline | default | on | exact | 9 bits |
| riscv64 | 6.6.144 | mainline | kptr-hidden | on | exact | 9 bits |
| riscv64 | 6.6.144 | mainline | perf-open | on | exact | 9 bits |
| riscv64 | 6.6.144 | mainline | dmesg-open | on | exact | 9 bits |
| riscv64 | 6.6.144 | mainline | bpf-open | on | exact | 9 bits |
| riscv64 | 6.6.144 | mainline | hardened | on | 3 bits | 9 bits |
| riscv64 | 6.6.144 | mainline | no5lvl | on | exact | 9 bits |
| riscv64 | 6.6.144 | mainline | no4lvl | on | exact | 9 bits |
| riscv64 | 7.0.0 | mainline | default | on | 16 bits | 9 bits |
| riscv64 | 7.0.0 | mainline | kptr-hidden | on | 16 bits | 9 bits |
| riscv64 | 7.0.0 | mainline | perf-open | on | exact | 9 bits |
| riscv64 | 7.0.0 | mainline | dmesg-open | on | 16 bits | 9 bits |
| riscv64 | 7.0.0 | mainline | bpf-open | on | 16 bits | 9 bits |
| riscv64 | 7.0.0 | mainline | hardened | on | 16 bits | 9 bits |
| riscv64 | 7.0.0 | mainline | no5lvl | on | 16 bits | 9 bits |
| riscv64 | 7.0.0 | mainline | no4lvl | on | 16 bits | 9 bits |
| s390x | 6.12.81-0-lts | alpine | default | on | 39 bits | 10 bits |
| s390x | 6.12.81-0-lts | alpine | kptr-hidden | on | 39 bits | 10 bits |
| s390x | 6.12.81-0-lts | alpine | perf-open | on | exact | 10 bits |
| s390x | 6.12.81-0-lts | alpine | dmesg-open | on | 39 bits | 10 bits |
| s390x | 6.12.81-0-lts | alpine | bpf-open | on | 28 bits | 10 bits |
| s390x | 6.12.81-0-lts | alpine | hardened | on | 39 bits | 10 bits |
| s390x | 5.15.211 | mainline | default | on | 17 bits | 10 bits |
| s390x | 5.15.211 | mainline | kptr-hidden | on | 17 bits | 10 bits |
| s390x | 5.15.211 | mainline | perf-open | on | exact | exact |
| s390x | 5.15.211 | mainline | dmesg-open | on | 17 bits | 10 bits |
| s390x | 5.15.211 | mainline | bpf-open | on | 17 bits | 10 bits |
| s390x | 5.15.211 | mainline | hardened | on | 17 bits | 10 bits |
| s390x | 6.6.144 | mainline | default | on | 17 bits | 10 bits |
| s390x | 6.6.144 | mainline | kptr-hidden | on | 17 bits | 10 bits |
| s390x | 6.6.144 | mainline | perf-open | on | exact | exact |
| s390x | 6.6.144 | mainline | dmesg-open | on | 17 bits | 10 bits |
| s390x | 6.6.144 | mainline | bpf-open | on | 17 bits | 10 bits |
| s390x | 6.6.144 | mainline | hardened | on | 17 bits | 10 bits |
| s390x | 7.0.0 | mainline | default | on | 39 bits | 10 bits |
| s390x | 7.0.0 | mainline | kptr-hidden | on | 39 bits | 10 bits |
| s390x | 7.0.0 | mainline | perf-open | on | exact | 10 bits |
| s390x | 7.0.0 | mainline | dmesg-open | on | 39 bits | 10 bits |
| s390x | 7.0.0 | mainline | bpf-open | on | 39 bits | 10 bits |
| s390x | 7.0.0 | mainline | hardened | on | 39 bits | 10 bits |
| x86_64 | 6.12.81-0-virt | alpine | default | on | 2 bits | 6 bits |
| x86_64 | 6.12.81-0-virt | alpine | kptr-hidden | on | 2 bits | 6 bits |
| x86_64 | 6.12.81-0-virt | alpine | perf-open | on | exact | 6 bits |
| x86_64 | 6.12.81-0-virt | alpine | dmesg-open | on | exact | 6 bits |
| x86_64 | 6.12.81-0-virt | alpine | bpf-open | on | exact | 6 bits |
| x86_64 | 6.12.81-0-virt | alpine | hardened | on | 2 bits | 6 bits |
| x86_64 | 6.12.81-0-virt | alpine | no5lvl | on | 2 bits | 6 bits |
| x86_64 | 6.12.81-0-virt | alpine | la57 | on | exact | 6 bits |
| x86_64 | 5.15.211 | mainline | default | on | 5 bits | 9 bits |
| x86_64 | 5.15.211 | mainline | kptr-hidden | on | 5 bits | 9 bits |
| x86_64 | 5.15.211 | mainline | perf-open | on | exact | 9 bits |
| x86_64 | 5.15.211 | mainline | dmesg-open | on | 5 bits | 9 bits |
| x86_64 | 5.15.211 | mainline | bpf-open | on | 5 bits | 9 bits |
| x86_64 | 5.15.211 | mainline | hardened | on | 5 bits | 9 bits |
| x86_64 | 5.15.211 | mainline | no5lvl | on | 5 bits | 9 bits |
| x86_64 | 5.15.211 | mainline | la57 | on | 5 bits | 9 bits |
| x86_64 | 6.6.144 | mainline | default | on | 5 bits | 9 bits |
| x86_64 | 6.6.144 | mainline | kptr-hidden | on | 5 bits | 9 bits |
| x86_64 | 6.6.144 | mainline | perf-open | on | exact | 9 bits |
| x86_64 | 6.6.144 | mainline | dmesg-open | on | 5 bits | 9 bits |
| x86_64 | 6.6.144 | mainline | bpf-open | on | 5 bits | 9 bits |
| x86_64 | 6.6.144 | mainline | hardened | on | 5 bits | 9 bits |
| x86_64 | 6.6.144 | mainline | no5lvl | on | 5 bits | 9 bits |
| x86_64 | 6.6.144 | mainline | la57 | on | 5 bits | 9 bits |
| x86_64 | 7.0.0 | mainline | default | on | 9 bits | 9 bits |
| x86_64 | 7.0.0 | mainline | kptr-hidden | on | 9 bits | 9 bits |
| x86_64 | 7.0.0 | mainline | perf-open | on | exact | 9 bits |
| x86_64 | 7.0.0 | mainline | dmesg-open | on | 9 bits | 9 bits |
| x86_64 | 7.0.0 | mainline | bpf-open | on | 9 bits | 9 bits |
| x86_64 | 7.0.0 | mainline | hardened | on | 9 bits | 9 bits |
| x86_64 | 7.0.0 | mainline | no5lvl | on | 9 bits | 9 bits |
| x86_64 | 7.0.0 | mainline | la57 | on | 9 bits | 9 bits |
What the profiles do is described in §2 above; what the table adds is where a profile makes no difference, and why.
On a stock kernel default is not exact: at the upstream
perf_event_paranoid=2, /proc/kallsyms is zeroed even with kptr_restrict=0
(kallsyms_show_value() needs perf_event_paranoid<=1 or CAP_SYSLOG), so the
base comes from sound inference alone. kptr-hidden therefore lands on the same
numbers — it removes a source that was already dark.
perf-open is the profile that recovers the base, and it does so on every
KASLR-on cell whose kernel is built with CONFIG_PERF_EVENTS. The MIPS cells
are the exception, and they show why its two unlocks are really one lever:
malta_defconfig does not set CONFIG_PERF_EVENTS, so perf_event_open is
absent — and kallsyms_show_value() reaches its kallsyms_for_perf() shortcut
only inside that same #ifdef, leaving kptr_restrict=0 to fall through to a
CAP_SYSLOG check an unprivileged reader fails. The symbol table is readable
and wholly zeroed at once, the sysctl the profile writes does not exist, and
every profile reports the same window there.
The phys residual moves independently, because /proc/iomem zeroes its
Kernel code addresses for any non-root reader regardless of kptr_restrict or
perf_event_paranoid. So the physical axis is largely unmoved by the profiles:
the perf unlock recovers the virtual text base, not the physical one.
Architectures showing KASLR off receive no seed under the default qemu machine
(or the port has no text KASLR), so the kernel boots unrandomized and residual
reads —. KASLD still bounds the fixed base soundly via the disabled-base path;
it is simply not a KASLR-defeat result, so the column does not score it. riscv64
is the mixed case: its text KASLR needs both a kernel built for it and a
boot-supplied seed, so the mainline 6.6 and 7.0 cells are built with
RANDOMIZE_BASE and booted with a per-boot kaslr-seed spliced into the device
tree, while 5.15 (predating riscv KASLR) and the Alpine kernel stay honestly
off.
On x86_64, CONFIG_RANDOMIZE_MEMORY randomizes the direct-map
(page_offset_base), vmalloc (vmalloc_base) and vmemmap (vmemmap_base)
region bases independently of the kernel text. Each live boot captures their true
values from /proc/kcore — the resolved variable's word, never the
terabyte-sparse whole — as ground truth, and gates the engine's resolved window
for each against it. A cell whose region window excludes the true base is withheld
exactly like a text-base violation; this gate runs in the live matrix and offline
via validate-bundle, and every gated cell is sound.
The per-region residual entropy is not yet tabulated here: the harness gates each region window for soundness but does not yet emit its residual bit-count. That is a reporting gap, not a soundness gap — the windows are checked to contain the truth today; only the "how many bits survived" number for these three quantities is still to be surfaced.
Scope (see Architecture characterization):
these three region bases are independently randomized only under
CONFIG_RANDOMIZE_MEMORY, i.e. x86_64. The other decoupled architectures
randomize the linear map by a different mechanism (aarch64 memstart_addr,
s390 identity base, riscv64 kernel_map.page_offset), which the engine
bounds but this harness does not ground-truth; a leak of one of those bases only
makes the reported residual an upper bound, never unsound. On coupled
architectures the direct map is a static projection of kernel text and carries no
independent base to randomize.
The matrix covers this axis in part. A kernel's paging mode and virtual-address
width — x86_64 4- vs 5-level paging, aarch64 VA36 / VA39 / VA42 / VA47
alongside the default VA48, the 32-bit VMSPLIT variants — shift the layout
windows and are a distinct axis from the kernel line. This axis is a direct test
of the engine's structural soundness: a wrong paging assumption or an
over-coarse alignment moves the image-base window, which is exactly the window
the soundness gate already checks (it is how past VA-bits and alignment bugs
surfaced), so each configuration cell is another soundness data point.
On aarch64 the width fixes the page size, so those four cells also span all
three granules — VA39 at 4K, VA36 and VA47 at 16K, VA42 at 64K. VA52 is the one
width left uncovered. x86_64 and riscv64 reach the same axis from a single
image through the no5lvl / no4lvl / la57 boot parameters, and armv7 and
i686 carry a purpose-built 2G/2G split against the default 3G/1G. The
configuration axes still outside the matrix are listed under Scope.
residual above is the guaranteed window — resolved only from signals at or
above the sound floor, and gate-checked to contain the truth. The engine also
resolves a likely window: the guaranteed window narrowed further by sub-floor
signals (timing side-channels, a single leaked pointer, config defaults). It is a
best-guess, always a subset of guaranteed, and — unlike guaranteed — it is not
gated to contain the truth. It surfaces in -j JSON as the likely /
likely_physical objects, emitted only when strictly tighter than guaranteed.
tests/vm/run spec-table lists the cells where the likely window beats
guaranteed, and by default only the reproducible narrowings: deterministic
parsed signals — a file, a leaked pointer, a config default — that beat the
sound floor the same way on every run.
Which cells appear, and which component supplies each, is not recorded here. Membership turns over as leaks are patched upstream and as the kernel matrix moves, so the command is the answer; a list transcribed into this document would describe kernels that are no longer in the run.
The shape, however, is stable, and it is the expected outcome once the profiles
and rules are correct: few rows, and only from a narrow band of signal. Where a
perf or kallsyms signal exists it is a sound pin, so it resolves the
guaranteed window under perf-open (to exact), never the likely one — a
sound signal has nothing left to narrow. And the memory-map heuristics that bound
RAM from world-readable facts (/proc/zoneinfo spans, the device-tree memory
node, dmesg zone lines) never tighten past the placement-tracking guaranteed
ceiling on any arch here, so they add no likely-only row either. Most parsed
signals thus land in the guaranteed matrix or contribute nothing; a signal shows
up here only when it is reproducible AND sub-floor, which is a narrow band to
occupy. The BPF verifier-log table is the counter-example worth noting: it used
to be listed here, and no longer is, because its match now resolves the
guaranteed window itself under bpf-open — a signal that graduates out of
likely is the outcome to want.
Absolute recovery is monotonic in how much the profile relaxes: perf-open
recovers the most, hardened the least, with default/kptr-hidden/dmesg-open
between — read that off the guaranteed matrix above, not the likely window.
Timing side-channel narrowings are excluded by default, and no figure for them
is published here. The likely window can also be narrowed by a
microarchitectural side channel — a cache or speculation oracle such as
prefetch or entrybleed that survives even perf_event_paranoid=3, pinning
the base where no parsed signal can. Such rows do appear in these runs, and they
are withheld for two independent reasons.
The first is reproducibility, and it is the whole reason the two are treated
differently. A parsed narrowing reproduces on any machine that boots the same
image: the signal is in the file, so the reader gets the same answer as the run
that produced it. A timing oracle's success is instead a function of the host
CPU — on a mitigated part (microcode or hypervisor Spectre-v2 defences) or a
lower microarchitecture the prefetch signal flattens, and the same cell narrows
less or not at all. It varies run to run on one machine, so a number derived from
one run is not a result a reader can check.
The second is that this harness is
the wrong instrument for the measurement. The x86-64 cells boot under KVM, where a
prefetch-timing signal can be an artifact of nested paging rather than the
behaviour it is taken to demonstrate — so a bit-count obtained here would
characterise the hypervisor as much as the technique, in either direction.
Neither reason is that the technique fails; these channels are validated
separately, against hardware, and are out of the matrix's scope (see
Scope). tests/vm/run spec-table --with-timing will list them for
anyone reproducing the run locally, with the same caveat attached.
The repository ships captured snapshots from real kernels, so the engine can be exercised across hardware that is not otherwise available:
make check # unit + integration tests (the per-rule checks)
tests/replay # run kasld over every captured fixture (structural)
make test-fixtures # assert the resolved window contains the truth, per capturetests/replay confirms KASLD parses and runs cleanly on each snapshot;
make check runs the per-rule unit tests, including the soundness checks.
make test-fixtures (tests/validate-fixtures) is the boot-free soundness gate:
it runs extra/validate-bundle over every captured kernel that carries ground
truth and asserts truth ∈ [min, max], catching the "window excludes the real
base" class of bug without a VM. Captures with no recorded truth report N/A
rather than pass. The corpus spans 13 architecture variants across
distributions (Alpine, Debian, Ubuntu/Raspbian) and kernels from 4.14 to 7.0:
| family | example kernels |
|---|---|
| x86_64, i686 | 5.15 – 7.0 (Alpine, Debian, Ubuntu) |
| aarch64, armv7 | 4.14 – 7.0 |
| ppc64, ppc32 | 5.15 – 7.0 |
| riscv64, riscv32 | 5.10 – 6.18 |
| s390x | 5.15 – 6.19 |
| loongarch64 | 6.18 |
| mips32, mips32el, mips64el | 4.19 – 6.15 |
These checks cover soundness — whether the inferred range contains the truth —
across a broad set of architectures, kernel versions, and reader-privilege
levels. They use one stock kernel configuration per architecture, plus
purpose-built cells on the two configuration axes that relocate the kernel
memory map: VA-bits / paging mode (aarch64 36/39/42/47, and x86_64 /
riscv64 via no5lvl / no4lvl / la57 from a single image) and VMSPLIT
(armv7 and i686 at 2G/2G). Those are the axes a wrong answer would be
silent on, because every other cell is built at the architecture's default
split and paging mode, so a model that assumed the default would pass the whole
matrix by coincidence. The remaining configuration axes — endianness, and
individual CONFIG_* toggles — are not covered. Timing and side-channel
components are validated separately, as their behaviour depends on hardware
rather than configuration.
The matrix exercises the kernel, not a distribution's userland. Each cell
boots straight into the analysis harness from a minimal initramfs, so no distro
init, sysctl.d drop-ins, service sandboxing, or LSM policy (AppArmor / SELinux /
seccomp) ever runs. Every profile therefore measures the kernel's own posture —
its compile-time sysctl defaults plus the one explicit sysctl vector the harness
sets — and nothing a userland would layer on top. This cuts both ways and is a
scope boundary in both directions: a real distribution install may enforce
controls these cells do not (so an actual system can be stricter than even the
hardened column), and userland is itself a potential leak surface — setuid
helpers, privileged daemons, and files a running service populates — that the
matrix does not exercise (so an actual system may expose more than the
default column). The isolation is deliberate: it attributes each result to a
named kernel and a declared sysctl vector, keeping the cells reproducible and
independent of any particular distribution's userspace.
Neither an unprivileged user namespace nor unprivileged eBPF widens
text-base recovery, so neither is a scenario of its own. A user namespace does
not help because the symbol table, /proc/iomem, dmesg, and
perf_event_open gates all check capable() in the initial user namespace,
which the ns_capable() a child namespace grants cannot satisfy — so the
default and hardened results already bound a userns-wielding attacker for the
text base. A user namespace reaches only ns_capable-gated leaks through created
virtual devices (a third-party-driver ioctl surface), which the minimal
initramfs does not expose.
eBPF does not help either. Where it is enabled
(kernel.unprivileged_bpf_disabled=0; the upstream default flipped to disabled
in 5.16), the disclosure KASLD does exploit is the verifier log printing an
unmasked struct bpf_map * — a direct-map virtual address, not the text base.
Like a %pK /proc/net socket pointer, it bounds page_offset / the direct map
— a memory-KASLR region this matrix does not score — so it leaves the
text-image-base residual unchanged (the relevant verifier-log hole is
version-specific and was masked in 7.2). Both are disclosure channels in their
own right, but of kernel data addresses, not the text base measured here.
These are limits on what the checks here verify. For what a KASLD result means when it is run against a target — in particular why a failure to recover the base is not evidence the system is secure — see limitations.md.