Skip to content

Commit 127a040

Browse files
committed
arch: make every arch declare its image-base residue
image_base_grid_align snaps a resolved _text window to the KASLR grid, which is sound only where _text's offset within its alignment granule is a constant. Get that offset wrong and raising a floor lands past the true base, dropping the truth out of the GUARANTEED window — the one thing that window promises. IMAGE_BASE_RESIDUE_FIXED gated it, and defaulted to "the residue is fixed". Sixteen of seventeen headers took that by silence: a claim about a kernel's linker layout that nobody had made. Only arm32 had ever answered, and only because someone hit the case where it is false. There is no defensible default here, so there is no longer one. The macro joins the mandatory-macro block beside LINEAR_MAP_ANCHOR and TEXT_TRACKS_DIRECTMAP, and a header that omits it fails to compile. The contract now also asks WHICH basis the answer rests on, because two very different claims were collapsed into one boolean. By construction: the residue is zero, or the arch models no KASLR, so nothing can drift. Observed: the residue is non-zero and emerges from something the kernel is free to change, where agreeing builds are evidence and not a guarantee. riscv is the second case and is now 0. Its _text is not the load address — the linker emits _start, then HEAD_TEXT_SECTION, then ALIGN(PAGE_SIZE) — so the residue is roundup(sizeof .head.text, PAGE_SIZE), and that section is #ifdef CONFIG_EFI / RISCV_M_MODE / MMU code whose size is not fixed. Three captures agree at 0x2000 (alpine 6.18, debian 6.12, riscv32 mainline 6.15); that is not the bar this switch asks for. No output changes, measured rather than assumed: toggling riscv64 between 1 and 0 and diffing full JSON across every riscv64 fixture gives identical results, because image_base_grid_align emits no constraint on any fixture in the corpus. The rule's own unit tests are what cover it.
1 parent ed34f54 commit 127a040

12 files changed

Lines changed: 80 additions & 5 deletions

File tree

src/include/kasld/api.h

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,10 @@ __extension__ _Static_assert((unsigned long)KERNEL_PHYS_MAX >
393393
#error \
394394
"arch header must define LINEAR_MAP_ANCHOR (LM_ANCHOR_PHYS_OFFSET / _DRAM_BASE / _UNKNOWABLE)"
395395
#endif
396+
#ifndef IMAGE_BASE_RESIDUE_FIXED
397+
#error \
398+
"arch header must define IMAGE_BASE_RESIDUE_FIXED (0 or 1) -- see its contract later in this header"
399+
#endif
396400

397401
/* Is an address inside [KERNEL_VIRT_TEXT_MIN, KERNEL_VIRT_TEXT_MAX] necessarily
398402
* part of the kernel image or a module, so that ruling out the module band
@@ -841,11 +845,27 @@ static inline unsigned long kasld_page_offset_if_known(void) {
841845
* true where KASLR places the base on the grid, or a fixed linker offset
842846
* guarantees it. 0 where the offset is config-dependent (arm32: TEXT_OFFSET
843847
* varies by config and _stext is padded to the section boundary), so snapping
844-
* could floor a bound below the true base. Default 1 (the residue is fixed);
845-
* arches whose residue is not fixed set 0. */
846-
#ifndef IMAGE_BASE_RESIDUE_FIXED
847-
#define IMAGE_BASE_RESIDUE_FIXED 1
848-
#endif
848+
* could floor a bound below the true base.
849+
*
850+
* DECLARED BY EVERY ARCH HEADER; there is no default. A permissive one is the
851+
* wrong way round: the failure it admits is a bound raised PAST the true base,
852+
* which drops the truth out of the guaranteed window -- the one thing that
853+
* window promises. Sixteen of seventeen headers once took "the residue is
854+
* fixed" by silence, which is a claim about a kernel's linker layout that
855+
* nobody had made.
856+
*
857+
* State WHICH basis the answer rests on, because they are not equally strong:
858+
*
859+
* by construction -- the residue is zero (the image starts on the granule),
860+
* or the arch models no KASLR so the base is the compile-time default and
861+
* the residue is right by definition. Nothing can drift.
862+
* observed -- the residue is non-zero and emerges from something the
863+
* kernel is free to change, such as the size of a boot-code section. Real
864+
* builds agreeing is evidence, not a guarantee, and 0 is the answer unless
865+
* the sharpening is worth the risk.
866+
*
867+
* Enforced by the mandatory-macro block near the top of this header; there is
868+
* deliberately no #define here to fall back to. */
849869

850870
/* Physical firmware load offset (DRAM base -> phys image base). 0 where
851871
* firmware loads the image at the DRAM base; riscv64 overrides to 2 MiB

src/include/kasld/arch/arm64.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,4 +402,8 @@ static inline unsigned long arm64_page_end_for(unsigned long va_bits) {
402402

403403
#define KASLR_SUPPORTED 1
404404

405+
// Residue 0: KIMAGE_VADDR plus a MIN_KIMG_ALIGN-aligned slide, so _text sits on
406+
// the granule. The 0x10000 head gap is _stext's (STEXT_OFFSET), not the base's.
407+
#define IMAGE_BASE_RESIDUE_FIXED 1
408+
405409
#endif /* KASLD_ARM64_H */

src/include/kasld/arch/loongarch64.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,4 +168,8 @@ static inline unsigned long arch_default_phys_text_base(void) {
168168

169169
#define KASLR_SUPPORTED 1
170170

171+
// Residue 0: the 2 MiB image offset is a whole number of 64 KiB granules. The
172+
// 0x20000 head gap is _stext's (STEXT_OFFSET), not the base's.
173+
#define IMAGE_BASE_RESIDUE_FIXED 1
174+
171175
#endif /* KASLD_LOONGARCH64_H */

src/include/kasld/arch/mips32.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,8 @@
126126

127127
#define KASLR_SUPPORTED 1
128128

129+
// Residue 0: _text IS the linker load address (see IMAGE_BASE_OFFSET above),
130+
// and KASLR relocates by whole 64 KiB granules, so it stays on the grid.
131+
#define IMAGE_BASE_RESIDUE_FIXED 1
132+
129133
#endif /* KASLD_MIPS32_H */

src/include/kasld/arch/mips64.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,8 @@
112112

113113
#define KASLR_SUPPORTED 1
114114

115+
// Residue 0: _text IS the linker load address (see IMAGE_BASE_OFFSET above),
116+
// and KASLR relocates by whole 64 KiB granules, so it stays on the grid.
117+
#define IMAGE_BASE_RESIDUE_FIXED 1
118+
115119
#endif /* KASLD_MIPS64_H */

src/include/kasld/arch/ppc32.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,7 @@
137137

138138
#define KASLR_SUPPORTED 1
139139

140+
// Residue 0: _text is on the granule; the grid is plain alignment.
141+
#define IMAGE_BASE_RESIDUE_FIXED 1
142+
140143
#endif /* KASLD_PPC32_H */

src/include/kasld/arch/ppc64.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,8 @@
114114
// PPC64 does not have mainline KASLR.
115115
#define KASLR_SUPPORTED 0
116116

117+
// Residue 0, and no KASLR modelled here -- the base is the compile-time
118+
// default, so the residue is right by definition either way.
119+
#define IMAGE_BASE_RESIDUE_FIXED 1
120+
117121
#endif /* KASLD_PPC64_H */

src/include/kasld/arch/riscv32.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,10 @@
103103
// RISC-V 32-bit does not have mainline KASLR.
104104
#define KASLR_SUPPORTED 0
105105

106+
// Same basis as riscv64: the residue is roundup(sizeof .head.text, PAGE_SIZE),
107+
// not a constructed constant. No KASLR is modelled here, so nothing turns on
108+
// it today -- declared 0 so the answer matches the reason rather than the
109+
// consequence.
110+
#define IMAGE_BASE_RESIDUE_FIXED 0
111+
106112
#endif /* KASLD_RISCV32_H */

src/include/kasld/arch/riscv64.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,4 +293,19 @@ static inline unsigned long arch_default_text_base(void) {
293293

294294
#define KASLR_SUPPORTED 1
295295

296+
// OBSERVED, not constructed -- so 0.
297+
//
298+
// _text is not the load address here: the linker emits _start, then
299+
// HEAD_TEXT_SECTION, then ALIGN(PAGE_SIZE), then _text. The residue is
300+
// therefore roundup(sizeof .head.text, PAGE_SIZE), and that section is built
301+
// from #ifdef CONFIG_EFI / RISCV_M_MODE / MMU code whose size the kernel is
302+
// free to change. Three captures agree at 0x2000 (alpine 6.18, debian 6.12,
303+
// riscv32 mainline 6.15), which is evidence and not a guarantee.
304+
//
305+
// Snapping on a residue that is one page out raises a floor past the true
306+
// base, dropping the truth from the GUARANTEED window. Measured cost of
307+
// declining: nil -- image_base_grid_align emits no constraint on any fixture.
308+
// arch/riscv/kernel/vmlinux.lds.S, arch/riscv/kernel/head.S
309+
#define IMAGE_BASE_RESIDUE_FIXED 0
310+
296311
#endif /* KASLD_RISCV64_H */

src/include/kasld/arch/s390.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,10 @@
227227

228228
#define KASLR_SUPPORTED 1
229229

230+
// Residue 0: the 1 MiB image offset is a whole number of 16 KiB granules, so
231+
// _text still lands on the grid.
232+
#define IMAGE_BASE_RESIDUE_FIXED 1
233+
230234
/* s390 opts OUT of the generic compile-time-default disabled-pin: the no-KASLR
231235
* image base is LAYOUT-DEPENDENT (modern high CONFIG_KERNEL_IMAGE_BASE vs
232236
* pre-v6.8 low identity-mapped, ~4 TiB apart) and CONFIG_KERNEL_IMAGE_BASE is

0 commit comments

Comments
 (0)