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