Skip to content

Commit 609434b

Browse files
committed
s390: fix module_text_bound — add Case B for modules-below-start layout
s390 modules sit below __kaslr_offset (image start), not _end, so module addresses constrain text_base in both directions. - s390.h: set MODULES_RELATIVE_TO_TEXT=1 (was 0), add MODULES_BELOW_TEXT_START=1, and MODULES_END_TO_TEXT_OFFSET=0x801FC000 (MODULES_LEN + (_SEGMENT_SIZE - KERNEL_ALIGN) + TEXT_OFFSET — the extra term accounts for round_down(__kaslr_offset, _SEGMENT_SIZE)) - module_text_bound.c: add Case B to tighten both text_base_max (from vmod_lo) and text_base_min (from vmod_hi) on s390
1 parent dcf38b4 commit 609434b

2 files changed

Lines changed: 112 additions & 28 deletions

File tree

src/include/arch/s390.h

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,41 @@
8585
#define KERNEL_BASE_MIN 0x100000ul
8686
#define KERNEL_BASE_MAX 0x20000000000000ul
8787

88-
// Modules: 2 GiB (MODULES_LEN = 1 << 31) placed just below kernel text.
89-
// MODULES_END = round_down(__kaslr_offset, _SEGMENT_SIZE).
88+
// Modules: 2 GiB (MODULES_LEN = 1 << 31) placed immediately below the kernel
89+
// image base. MODULES_END = round_down(__kaslr_offset, _SEGMENT_SIZE) ≈
90+
// __kaslr_offset = _stext - TEXT_OFFSET. MODULES_START = MODULES_END - 2 GiB.
91+
//
92+
// Any observed module address vmod constrains text_base in both directions:
93+
// Upper bound (from minimum vmod_lo):
94+
// MODULES_END = round_down(__kaslr_offset, _SEGMENT_SIZE) ≤ vmod_lo +
95+
// MODULES_LEN
96+
// __kaslr_offset is KERNEL_ALIGN-aligned, so the gap
97+
// __kaslr_offset − MODULES_END ∈ [0, _SEGMENT_SIZE − KERNEL_ALIGN]
98+
// __kaslr_offset ≤ vmod_lo + MODULES_LEN + (_SEGMENT_SIZE − KERNEL_ALIGN)
99+
// _stext ≤ vmod_lo + MODULES_LEN + (_SEGMENT_SIZE − KERNEL_ALIGN) +
100+
// TEXT_OFFSET
101+
// Lower bound (from maximum vmod_hi):
102+
// __kaslr_offset > vmod_hi → __kaslr_offset ≥ align_up(vmod_hi,
103+
// KERNEL_ALIGN) _stext ≥ align_up(vmod_hi, KERNEL_ALIGN) + TEXT_OFFSET
104+
//
105+
// MODULES_BELOW_TEXT_START signals that MODULES_END is anchored to the image
106+
// start (_stext - TEXT_OFFSET), not to image end (_end) as on riscv64. The
107+
// module_text_bound plugin uses this flag to select the correct formula and to
108+
// derive a lower bound on text_base from the maximum observed module address.
109+
//
110+
// MODULES_END_TO_TEXT_OFFSET: upper-bound addend in module_text_bound.c:
111+
// new_max = (vmod_lo + MODULES_END_TO_TEXT_OFFSET) & ~(kaslr_align - 1)
112+
// = MODULES_LEN + (_SEGMENT_SIZE − KERNEL_ALIGN) + TEXT_OFFSET
113+
// = 0x80000000 + 0xFC000 + 0x100000 = 0x801FC000
114+
//
90115
// Runtime-determined; use wide bounds for validation.
91116
#define MODULES_START 0ul
92117
#define MODULES_END 0x20000000000000ul
93-
#define MODULES_RELATIVE_TO_TEXT 0
118+
#define MODULES_RELATIVE_TO_TEXT 1
119+
#define MODULES_BELOW_TEXT_START 1
120+
#define MODULES_END_TO_TEXT_OFFSET \
121+
0x801FC000ul /* MODULES_LEN + (_SEGMENT_SIZE - KERNEL_ALIGN) + TEXT_OFFSET \
122+
*/
94123

95124
// Virtual KASLR granularity: THREAD_SIZE (16 KiB on s390, PAGE_SIZE << 2).
96125
// Physical placement uses _SEGMENT_SIZE (1 MiB), but virtual text addresses

src/inference/module_text_bound.c

Lines changed: 80 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,43 @@
11
// This file is part of KASLD - https://github.com/bcoles/kasld
22
//
3-
// Inference plugin: module region → text upper bound (POST_COLLECTION)
3+
// Inference plugin: module region → text bounds (POST_COLLECTION)
44
//
55
// Active only on architectures where the module region is anchored to the
6-
// kernel image (MODULES_RELATIVE_TO_TEXT == 1; currently riscv64). On all
7-
// other architectures the module range is fixed or PAGE_OFFSET-relative and
8-
// carries no information about the text KASLR slot; the plugin is a no-op.
6+
// kernel image (MODULES_RELATIVE_TO_TEXT == 1). Two sub-cases:
97
//
10-
// On riscv64 the module region starts at PFN_ALIGN(_end) - 2 GiB, so:
8+
// Case A — modules below _end (riscv64, MODULES_BELOW_TEXT_START undefined):
9+
// MODULES_END ≈ _end (image end). Module_lo ≥ _end - MODULES_LEN, so:
1110
//
12-
// _end ≈ module_lo + MODULES_END_TO_TEXT_OFFSET
11+
// _end ≤ module_lo + MODULES_END_TO_TEXT_OFFSET
12+
// text_base ≤ _end - MIN_KERNEL_IMAGE_SIZE
13+
// ≤ module_lo + MODULES_END_TO_TEXT_OFFSET -
14+
// MIN_KERNEL_IMAGE_SIZE
1315
//
14-
// Since module_lo ≥ MODULES_VADDR and MODULES_VADDR = PFN_ALIGN(_end) - 2G,
15-
// the estimate end_est = module_lo + 2G is always ≥ _end. Combined with the
16-
// kernel-size lower bound (MIN_KERNEL_IMAGE_SIZE), this gives a safe upper
17-
// bound on text_base:
16+
// Only text_base_max is tightened; text_base_min is not (a module far from
17+
// the top of the region gives no useful lower bound).
1818
//
19-
// text_base = _end - actual_size
20-
// ≤ end_est - MIN_KERNEL_IMAGE_SIZE
19+
// Case B — modules below image start (s390, MODULES_BELOW_TEXT_START == 1):
20+
// MODULES_END = round_down(__kaslr_offset, _SEGMENT_SIZE) ≤ __kaslr_offset.
21+
// __kaslr_offset is KERNEL_ALIGN-aligned, so the gap between MODULES_END and
22+
// __kaslr_offset is at most _SEGMENT_SIZE - KERNEL_ALIGN. Any module address
23+
// vmod satisfies vmod < MODULES_END ≤ __kaslr_offset, giving constraints in
24+
// both directions:
2125
//
22-
// The plugin therefore tightens ctx->text_base_max only. text_base_min is
23-
// not tightened: the observed module_lo may be far above MODULES_VADDR (if
24-
// few modules are loaded near the base of the region), which would make the
25-
// derived lower bound unsafe.
26+
// Upper bound (minimum module address vmod_lo):
27+
// MODULES_END ≤ vmod_lo + MODULES_LEN
28+
// __kaslr_offset ≤ vmod_lo + MODULES_LEN + (_SEGMENT_SIZE - KERNEL_ALIGN)
29+
// _stext ≤ vmod_lo + MODULES_END_TO_TEXT_OFFSET
30+
// (= MODULES_LEN + (_SEGMENT_SIZE - KERNEL_ALIGN) + TEXT_OFFSET)
2631
//
27-
// MIN_KERNEL_IMAGE_SIZE (4 MiB) matches the estimate used in
28-
// compute_derived_addrs() for rendering; undershooting is safe — it gives
29-
// a higher (less tight) upper bound rather than excluding the true text base.
32+
// Lower bound (maximum module address vmod_hi):
33+
// __kaslr_offset > vmod_hi
34+
// __kaslr_offset ≥ align_down(vmod_hi, kaslr_align) + kaslr_align
35+
// _stext ≥ align_down(vmod_hi, kaslr_align) + kaslr_align + TEXT_OFFSET
36+
//
37+
// Both text_base_max and text_base_min are tightened.
38+
//
39+
// MIN_KERNEL_IMAGE_SIZE (4 MiB, Case A only) matches the estimate used in
40+
// compute_derived_addrs() for rendering; undershooting is safe.
3041
// ---
3142
// <bcoles@gmail.com>
3243

@@ -48,21 +59,63 @@ static void module_text_bound_run(struct kasld_analysis_ctx *ctx) {
4859
unsigned long kaslr_align = ctx->arch->kaslr_align;
4960
unsigned long kaslr_min = ctx->arch->kaslr_base_min;
5061

51-
/* Find the minimum valid virtual module address. */
62+
/* Find minimum and maximum valid aligned virtual module addresses. */
5263
unsigned long vmod_lo = ULONG_MAX;
64+
unsigned long vmod_hi = 0;
5365
for (size_t i = 0; i < ctx->result_count; i++) {
5466
const struct result *r = &ctx->results[i];
55-
if (r->type == KASLD_ADDR_VIRT &&
56-
strcmp(r->section, KASLD_SECTION_MODULE) == 0 && r->valid &&
57-
r->aligned < vmod_lo)
67+
if (r->type != KASLD_ADDR_VIRT || !r->valid ||
68+
strcmp(r->section, KASLD_SECTION_MODULE) != 0)
69+
continue;
70+
if (r->aligned < vmod_lo)
5871
vmod_lo = r->aligned;
72+
if (r->aligned > vmod_hi)
73+
vmod_hi = r->aligned;
5974
}
6075

6176
if (vmod_lo == ULONG_MAX)
6277
return;
6378

64-
/* _end ≈ vmod_lo + MODULES_END_TO_TEXT_OFFSET (2 GiB on riscv64).
65-
* text_base ≤ end_est - MIN_KERNEL_IMAGE_SIZE is always safe (see header). */
79+
#if MODULES_BELOW_TEXT_START
80+
/* Case B: MODULES_END = round_down(__kaslr_offset, _SEGMENT_SIZE) (s390).
81+
* MODULES_END_TO_TEXT_OFFSET = MODULES_LEN + (_SEGMENT_SIZE - KERNEL_ALIGN)
82+
* + TEXT_OFFSET, accounting for the gap between MODULES_END and
83+
* __kaslr_offset. */
84+
unsigned long text_offset = ctx->arch->text_offset;
85+
86+
/* Upper bound: _stext ≤ vmod_lo + MODULES_END_TO_TEXT_OFFSET. */
87+
unsigned long new_max =
88+
(vmod_lo + MODULES_END_TO_TEXT_OFFSET) & ~(kaslr_align - 1);
89+
90+
if (new_max > kaslr_min && new_max > ctx->text_base_min &&
91+
new_max < ctx->text_base_max) {
92+
if (verbose && !quiet)
93+
fprintf(stderr,
94+
"[layout] text_base_max tightened by module_text_bound:"
95+
" %#lx -> %#lx (vmod_lo=%#lx)\n",
96+
ctx->text_base_max, new_max, vmod_lo);
97+
ctx->text_base_max = new_max;
98+
}
99+
100+
/* Lower bound: any module must be strictly below __kaslr_offset.
101+
* __kaslr_offset ≥ align_down(vmod_hi, kaslr_align) + kaslr_align
102+
* _stext ≥ align_down(vmod_hi, kaslr_align) + kaslr_align + TEXT_OFFSET */
103+
unsigned long new_min =
104+
(vmod_hi & ~(kaslr_align - 1)) + kaslr_align + text_offset;
105+
106+
if (new_min > ctx->text_base_min && new_min < ctx->text_base_max) {
107+
if (verbose && !quiet)
108+
fprintf(stderr,
109+
"[layout] text_base_min tightened by module_text_bound:"
110+
" %#lx -> %#lx (vmod_hi=%#lx)\n",
111+
ctx->text_base_min, new_min, vmod_hi);
112+
ctx->text_base_min = new_min;
113+
}
114+
115+
#else
116+
/* Case A: MODULES_END ≈ _end (riscv64).
117+
* _end ≈ vmod_lo + MODULES_END_TO_TEXT_OFFSET; text_base ≤ _end - MIN_size.
118+
*/
66119
unsigned long end_est = vmod_lo + MODULES_END_TO_TEXT_OFFSET;
67120
unsigned long new_max =
68121
(end_est - MIN_KERNEL_IMAGE_SIZE) & ~(kaslr_align - 1);
@@ -75,6 +128,8 @@ static void module_text_bound_run(struct kasld_analysis_ctx *ctx) {
75128
ctx->text_base_max, new_max, vmod_lo);
76129
ctx->text_base_max = new_max;
77130
}
131+
#endif /* MODULES_BELOW_TEXT_START */
132+
78133
#else
79134
(void)ctx;
80135
#endif /* MODULES_RELATIVE_TO_TEXT */

0 commit comments

Comments
 (0)