-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy pathdmesg_kaslr_disabled.c
More file actions
224 lines (207 loc) · 10.3 KB
/
Copy pathdmesg_kaslr_disabled.c
File metadata and controls
224 lines (207 loc) · 10.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
// This file is part of KASLD - https://github.com/bcoles/kasld
//
// Search kernel log for messages stating KASLR is disabled.
//
// Two distinct kernel-side states surface through nearly identical
// "KASLR disabled" / "KASLR is disabled" dmesg lines. The semantics are
// different and the engine consumes them via different scalar facts:
//
// (1) Definitive opt-out — kernel sits at KERNEL_VIRT_TEXT_DEFAULT.
// Triggers: user passed nokaslr (or kernel forced it off for an
// orthogonal reason like hibernation resume on x86, the kexec_file
// path on loongarch, etc.). The boot stub honoured the request and
// skipped relocation entirely.
// Wire fact: SF_VIRT_KASLR_DISABLED.
// Engine effect: virt_/phys_kaslr_disabled_pin pins Q_VIRT_IMAGE_BASE (and
// on coupled arches, Q_PHYS_IMAGE_BASE) to the arch's compile-time default.
//
// x86/x86_64:
// KASLR disabled: 'kaslr' not on cmdline (hibernation selected).
// KASLR disabled: 'nokaslr' on cmdline.
// ARM64:
// KASLR disabled on command line
// LoongArch:
// KASLR is disabled.
//
// (2) Randomization machinery failed — kernel was relocated by the
// boot stub but no random offset was applied. The resulting
// position is firmware-/boot-stub-deterministic but NOT the
// link-time default, so the engine MUST NOT pin to default from
// this signal.
// Wire facts: SF_VIRT_KASLR_RANDOMIZATION_FAILED +
// SF_PHYS_KASLR_RANDOMIZATION_FAILED. The four "KASLR disabled
// due to/...: CPU has no PRNG/...: not enough memory" reasons
// matched here fail both axes in the kernel's own boot stub, so
// both facts are emitted.
// Engine effect: hardening-report entropy downgrade (consumes
// SF_VIRT, since the user-visible "0 entropy" claim is about
// virt text), EFI loader-pool disambiguation tightening on EFI
// arches (consumes SF_PHYS), s390-specific phys_image_base
// inference (consumes SF_PHYS).
//
// ARM64 (arch/arm64/kernel/setup.c / kaslr.c — EFI stub):
// KASLR disabled due to lack of seed
// KASLR disabled due to FDT remapping failure
// S390 (arch/s390/boot/kaslr.c — boot stub):
// KASLR disabled: CPU has no PRNG
// KASLR disabled: not enough memory
//
// (3) EFI_RNG_PROTOCOL unavailable — PHYS-ONLY randomization failure.
// The EFI stub couldn't get random bytes from EFI_RNG_PROTOCOL,
// so phys placement falls back to the PE/COFF loader's choice
// (firmware-determined per machine). VIRTUAL KASLR is independent
// on EFI arm64 / riscv64 / loongarch64 — the DTB /chosen/kaslr-
// seed (or arch RNG: riscv64 Zkr, arm64 RNDR) feeds the virt
// offset separately and may have succeeded.
// Wire fact: SF_PHYS_KASLR_RANDOMIZATION_FAILED alone.
// Engine effect: efi_loader_kernel_pick disambiguates the phys
// placement to the lowest EFI_LOADER_CODE entry. The hardening
// posture does NOT downgrade (virt KASLR still has full entropy).
//
// EFI stub (drivers/firmware/efi/libstub/kaslr.c):
// EFI_RNG_PROTOCOL unavailable
//
// Introduced for ARM64 in kernel v5.5-rc1~22^2~11^9~1 on 2019-11-09:
// https://github.com/torvalds/linux/commit/294a9ddde6cdbf931a28b8c8c928d3f799b61cb5
//
// Detection component — does not leak an address.
//
// Requires:
// - kernel.dmesg_restrict = 0; or CAP_SYSLOG capabilities; or
// readable /var/log/dmesg.
//
// References:
// https://elixir.bootlin.com/linux/v5.19.17/source/arch/arm64/kernel/kaslr.c#L197
// https://elixir.bootlin.com/linux/v5.19.17/source/arch/arm64/kernel/kaslr.c#L200
// https://elixir.bootlin.com/linux/v6.1.6/source/arch/arm64/kernel/kaslr.c#L45
// https://elixir.bootlin.com/linux/v6.1.1/source/arch/s390/boot/kaslr.c#L35
// https://elixir.bootlin.com/linux/v6.1.1/source/arch/s390/boot/kaslr.c#L201
// https://elixir.bootlin.com/linux/v6.8.5/source/arch/loongarch/kernel/relocate.c#L107
// ---
// <bcoles@gmail.com>
#define _GNU_SOURCE
#include "include/dmesg.h"
#include "include/kasld/api.h"
#include "include/kasld/cli.h"
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
KASLD_EXPLAIN("Searches dmesg for messages indicating KASLR was disabled or "
"could not be enabled. Distinguishes three states: definitive "
"opt-out (nokaslr / hibernation / arch override) where the "
"kernel sits at the compile-time default text base; "
"randomization failure (missing entropy seed, no PRNG, "
"insufficient memory) where the kernel is at a firmware- or "
"boot-stub-determined position on both axes; and EFI_RNG_"
"PROTOCOL unavailable, where only PHYSICAL randomization "
"failed (virt KASLR via DTB seed / arch RNG is independent "
"and may have succeeded). Access is gated by dmesg_restrict.");
KASLD_META("method:detection\n"
"phase:inference\n"
"discloses:facts\n"
"sysctl:dmesg_restrict>=1\n"
"bypass:CAP_SYSLOG\n"
"fallback:/var/log/dmesg\n");
struct match_ctx {
bool opt_out; /* KASLR definitively off; kernel at link-time default. */
bool rand_failed; /* Boot stub tried, randomization did not run. */
bool efi_rng_unavailable; /* EFI stub couldn't get random bytes from */
/* EFI_RNG_PROTOCOL — phys placement only, */
/* virt KASLR independent via DTB seed/RNDR. */
};
static int on_match(const char *line, void *ctx) {
struct match_ctx *m = ctx;
if (!strstr(line, "KASLR disabled") && !strstr(line, "KASLR is disabled"))
return 1;
/* Randomization-machinery-failed variants. The boot stub relocated the
* kernel to a firmware-/boot-stub-determined position WITHOUT applying a
* random offset. This is NOT a pin-to-default signal — the kernel is not
* at KERNEL_VIRT_TEXT_DEFAULT. */
if (strstr(line, "KASLR disabled due to lack of seed") ||
strstr(line, "KASLR disabled due to FDT remapping failure") ||
strstr(line, "KASLR disabled: CPU has no PRNG") ||
strstr(line, "KASLR disabled: not enough memory")) {
m->rand_failed = true;
return 1;
}
/* A definitive opt-out (the kernel sits at the arch's compile-time default
* text base) is asserted ONLY for the specific phrases the boot stubs print
* for it: nokaslr / hibernation-selected on x86, the cmdline-confirming arm64
* line, and the loongarch "KASLR is disabled." An opt-out feeds the
* pin-to-default C_EQUALS via virt_/phys_kaslr_disabled_pin, so an
* unrecognized "KASLR disabled" line must NOT be taken as opt-out: a
* future/unknown randomization-failure reason would place the kernel at a
* firmware-determined position (not the default), and pinning to default
* would exclude the true base. Unknown variants emit nothing (a wide, honest
* window) rather than a wrong guaranteed pin. */
if (strstr(line, "'nokaslr'") || strstr(line, "'kaslr' not on cmdline") ||
strstr(line, "KASLR disabled on command line") ||
strstr(line, "KASLR is disabled")) {
m->opt_out = true;
}
return 1;
}
/* "EFI_RNG_PROTOCOL unavailable" — logged by the EFI stub when
* efi_get_random_bytes() returns EFI_NOT_FOUND. The stub sets
* efi_nokaslr = true, which disables PHYSICAL randomization only — the
* kernel stays at its PE/COFF loader placement (firmware-determined per
* machine, not the link-time default). VIRTUAL KASLR is independent on
* the affected arches (arm64 / riscv64 / loongarch64 EFI) and may still
* succeed via the DTB /chosen/kaslr-seed or an arch RNG (riscv64 Zkr,
* arm64 RNDR), so ONLY the phys-side fact is emitted. */
static int on_match_efi(const char *line, void *ctx) {
struct match_ctx *m = ctx;
if (strstr(line, "EFI_RNG_PROTOCOL unavailable"))
m->efi_rng_unavailable = true;
return 1;
}
int main(void) {
struct match_ctx m = {false, false, false};
kasld_info("searching dmesg for 'KASLR disabled' or 'KASLR is disabled' ...");
int ds = dmesg_search("KASLR ", on_match, &m);
kasld_info("searching dmesg for 'EFI_RNG_PROTOCOL unavailable' ...");
int ds2 = dmesg_search("EFI_RNG_PROTOCOL ", on_match_efi, &m);
if (!m.opt_out && !m.rand_failed && !m.efi_rng_unavailable) {
if (ds < 0 && ds2 < 0)
return KASLD_EXIT_NOPERM;
kasld_err("KASLR disabled indicator not found in dmesg");
return 0;
}
if (m.opt_out) {
kasld_info("Kernel was booted with KASLR disabled");
/* The dmesg "KASLR disabled" / "KASLR is disabled" lines fire from the
* boot stub's nokaslr path (or equivalent), which disables both virtual
* and physical randomization on every arch that emits them. */
kasld_emit_scalar(SF_VIRT_KASLR_DISABLED, 1, CONF_PARSED);
kasld_emit_scalar(SF_PHYS_KASLR_DISABLED, 1, CONF_PARSED);
}
if (m.rand_failed) {
kasld_info(
"Kernel attempted KASLR but randomization did not run "
"(firmware-/boot-stub-determined placement, not link-time default)");
/* Distinct from SF_*_KASLR_DISABLED: the boot stub still relocated the
* image, so KERNEL_VIRT_TEXT_DEFAULT cannot be pinned. Every dmesg-side
* "KASLR disabled" reason matched here ("lack of seed", "FDT
* remapping failure", "CPU has no PRNG", "not enough memory") fails
* BOTH the virt and the phys randomization paths in the boot stub's
* own code — neither axis got a random offset — so both facts are
* emitted. */
kasld_emit_scalar(SF_VIRT_KASLR_RANDOMIZATION_FAILED, 1, CONF_PARSED);
kasld_emit_scalar(SF_PHYS_KASLR_RANDOMIZATION_FAILED, 1, CONF_PARSED);
}
if (m.efi_rng_unavailable) {
kasld_info(
"EFI stub could not get random bytes from EFI_RNG_PROTOCOL "
"(physical placement is PE/COFF-loader-determined; virtual KASLR "
"may have succeeded independently via DTB seed or arch RNG)");
/* Phys-only randomization failure: emit SF_PHYS_KASLR_RANDOMIZATION_FAILED
* alone. virt-side facts are NOT emitted — virt KASLR on EFI arm64 /
* riscv64 / loongarch64 is independent of EFI_RNG_PROTOCOL and may have
* succeeded via the DTB seed. Consumed by efi_loader_kernel_pick (which
* disambiguates phys placement via the lowest EFI_LOADER_CODE entry
* when the stub fell back to deterministic allocation). */
kasld_emit_scalar(SF_PHYS_KASLR_RANDOMIZATION_FAILED, 1, CONF_PARSED);
}
return 0;
}