@@ -2905,23 +2905,23 @@ static int ilog2(unsigned long v) {
29052905 * projection with no engine dependencies); engine_resolve and its engine
29062906 * instance are engine-only (they drive the components + engine.c machinery). */
29072907static void engine_sync_authoritative (const struct engine * e );
2908- #ifndef KASLD_TESTING
2909- static void engine_resolve (struct engine * e );
2910- static struct engine
2911- g_auth_engine ; /* holds the GUARANTEED (primary) resolution */
29122908
29132909/* Sound floor for the guaranteed window: inputs below this are out of scope, so
29142910 * the window is derived purely from >= floor signals. CONF_INFERRED admits
29152911 * parsed/derived/inferred (proven); heuristic/timing/brute reach the likely
29162912 * window only. The two-window POLICY (which floors, what they mean) lives here;
2917- * the engine is floor-agnostic. */
2913+ * the engine is floor-agnostic.
2914+ *
2915+ * Outside the engine-only block: this names a confidence level and has no
2916+ * engine dependency, and the projection above -- which every build compiles --
2917+ * counts candidates at this floor. */
29182918#define KASLD_SOUND_FLOOR CONF_INFERRED
29192919
2920- #ifndef KASLD_TESTING
29212920/* Candidate count over q's honest compile-time top — the entropy this
29222921 * architecture's KASLR had before any evidence narrowed it. Counted through
29232922 * quantity_slots() at the same alignment as the residual, so the two are
2924- * directly comparable. */
2923+ * directly comparable. Its inputs are the quantity table and the sound floor;
2924+ * no engine state, so every build has it. */
29252925static unsigned long quantity_top_slots (enum kasld_quantity q ,
29262926 unsigned long align ) {
29272927 if (!align || !quantities [q ].init_top )
@@ -2930,7 +2930,11 @@ static unsigned long quantity_top_slots(enum kasld_quantity q,
29302930 quantities [q ].init_top (& top );
29312931 return quantity_slots (q , & top , KASLD_SOUND_FLOOR , NULL , 0 , align );
29322932}
2933- #endif
2933+
2934+ #ifndef KASLD_TESTING
2935+ static void engine_resolve (struct engine * e );
2936+ static struct engine
2937+ g_auth_engine ; /* holds the GUARANTEED (primary) resolution */
29342938
29352939/* Snapshot of the LIKELY resolution (floor CONF_BRUTE — all signals): the est +
29362940 * constraints quantity_slots() needs, plus the resolver's rejected-constraint
@@ -3100,27 +3104,12 @@ void compute_kaslr_info(struct summary *s) {
31003104 s -> kaslr .has_phys = 0 ;
31013105 s -> kaslr .pstext = observed_stext_base (KASLD_TYPE_PHYS , ptext );
31023106
3103- /* Hole-aware slot count: route via quantity_slots() so interior C_EXCLUDE
3104- * holes and any C_STRIDE residue class are reflected in the headline entropy
3105- * number. Flat (hi-lo)/align is the no-constraints fallback for KASLD_TESTING
3106- * builds (the engine instance is compiled out there). */
3107- #ifndef KASLD_TESTING
3108- s -> kaslr .vslots =
3109- quantity_slots (Q_VIRT_IMAGE_BASE , & g_auth_engine .est [Q_VIRT_IMAGE_BASE ],
3110- KASLD_SOUND_FLOOR , g_auth_engine .constraints ,
3111- g_auth_engine .n_constraints , layout .virt_kaslr_align );
3112- #else
3113- {
3114- unsigned long text_range =
3115- layout .virt_kaslr_text_max - layout .virt_kaslr_text_min ;
3116- /* Closed window: + 1 counts the floor slot, matching quantity_slots(). */
3117- s -> kaslr .vslots = (layout .virt_kaslr_align && layout .virt_kaslr_text_max )
3118- ? text_range / layout .virt_kaslr_align + 1
3119- : 0 ;
3120- }
3121- #endif
3107+ /* Hole-aware slot counts, projected beside the windows they count (see
3108+ * struct kasld_layout). Interior C_EXCLUDE holes and any C_STRIDE residue
3109+ * class are already reflected, so the headline entropy follows the estimate
3110+ * rather than the width of its convex hull. */
3111+ s -> kaslr .vslots = layout .virt_kaslr_slots ;
31223112 s -> kaslr .vbits = s -> kaslr .vslots > 0 ? ilog2 (s -> kaslr .vslots ) : 0 ;
3123- #ifndef KASLD_TESTING
31243113 {
31253114 /* The starting candidate count, from the window the kernel draws the image
31263115 * base from -- KASLR_VIRT_TEXT_MIN..MAX, not the quantity's honest top.
@@ -3141,31 +3130,15 @@ void compute_kaslr_info(struct summary *s) {
31413130 unsigned long top = (a && hi > lo ) ? (hi - lo ) / a + 1 : 0 ;
31423131 s -> kaslr .vtop_slots = top ;
31433132 s -> kaslr .vbits_top = top > 0 ? ilog2 (top ) : 0 ;
3144- #ifndef KASLD_TESTING
31453133 s -> kaslr .varch_slots = quantity_top_slots (Q_VIRT_IMAGE_BASE , a );
3146- #endif
31473134 }
3148- #endif
31493135
31503136#ifdef KASLR_PHYS_MIN
31513137 {
3152- #ifndef KASLD_TESTING
3153- s -> kaslr .pslots =
3154- quantity_slots (Q_PHYS_IMAGE_BASE , & g_auth_engine .est [Q_PHYS_IMAGE_BASE ],
3155- KASLD_SOUND_FLOOR , g_auth_engine .constraints ,
3156- g_auth_engine .n_constraints , layout .phys_kaslr_align );
3157- #else
3158- unsigned long phys_range =
3159- layout .phys_kaslr_text_max - layout .phys_kaslr_text_min ;
3160- s -> kaslr .pslots = (layout .phys_kaslr_align && layout .phys_kaslr_text_max )
3161- ? phys_range / layout .phys_kaslr_align + 1
3162- : 0 ;
3163- #endif
3138+ s -> kaslr .pslots = layout .phys_kaslr_slots ;
31643139 s -> kaslr .pbits = s -> kaslr .pslots > 0 ? ilog2 (s -> kaslr .pslots ) : 0 ;
3165- #ifndef KASLD_TESTING
31663140 s -> kaslr .parch_slots =
31673141 quantity_top_slots (Q_PHYS_IMAGE_BASE , layout .phys_kaslr_align );
3168- #endif
31693142 }
31703143#endif
31713144
@@ -3280,64 +3253,28 @@ void compute_kaslr_info(struct summary *s) {
32803253 ? layout .virt_module_base_max
32813254 : 0 ;
32823255
3283- /* Hole-aware residual slot counts for the memory-KASLR regions, mirroring the
3284- * headline vslots/pslots: routed through quantity_slots() so interior
3285- * C_EXCLUDE holes (and any stride class) are reflected in the entropy the
3286- * renderer prints, rather than a hole-blind (max-min)/align. Flat division is
3287- * the KASLD_TESTING fallback (the engine instance is compiled out there).
3288- * Only the both-sided window displays a slot count, so gate on min && max. */
3289- #ifndef KASLD_TESTING
3256+ /* Residual slot counts for the memory-KASLR regions, mirroring the headline
3257+ * vslots/pslots: the projected count already reflects interior C_EXCLUDE
3258+ * holes and any stride class, so the entropy the renderer prints follows the
3259+ * estimate rather than the width of its hull. Only a both-sided window
3260+ * displays a count, so gate on min && max -- a presentation rule, asked here
3261+ * where those edges are in hand. */
32903262 s -> kaslr .virt_page_offset_slots =
32913263 (s -> kaslr .virt_page_offset_min && s -> kaslr .virt_page_offset_max )
3292- ? quantity_slots (Q_PAGE_OFFSET , & g_auth_engine .est [Q_PAGE_OFFSET ],
3293- KASLD_SOUND_FLOOR , g_auth_engine .constraints ,
3294- g_auth_engine .n_constraints , RANDOMIZE_MEMORY_ALIGN )
3264+ ? layout .virt_page_offset_slots
32953265 : 0 ;
32963266 s -> kaslr .virt_vmalloc_slots =
32973267 (s -> kaslr .virt_vmalloc_min && s -> kaslr .virt_vmalloc_max )
3298- ? quantity_slots (Q_VMALLOC_BASE , & g_auth_engine .est [Q_VMALLOC_BASE ],
3299- KASLD_SOUND_FLOOR , g_auth_engine .constraints ,
3300- g_auth_engine .n_constraints , RANDOMIZE_MEMORY_ALIGN )
3268+ ? layout .virt_vmalloc_slots
33013269 : 0 ;
33023270 s -> kaslr .virt_vmemmap_slots =
33033271 (s -> kaslr .virt_vmemmap_min && s -> kaslr .virt_vmemmap_max )
3304- ? quantity_slots (Q_VMEMMAP_BASE , & g_auth_engine .est [Q_VMEMMAP_BASE ],
3305- KASLD_SOUND_FLOOR , g_auth_engine .constraints ,
3306- g_auth_engine .n_constraints , RANDOMIZE_MEMORY_ALIGN )
3272+ ? layout .virt_vmemmap_slots
33073273 : 0 ;
3308- /* The module base is page-granular on every arch that randomizes it (x86_64
3309- * draws a whole number of pages; the arm64 bounding box and the
3310- * PAGE_OFFSET-derived bands are page-aligned), so PAGE_SIZE is the pitch
3311- * rather than RANDOMIZE_MEMORY_ALIGN, which is an x86_64 memory-KASLR
3312- * constant and 0 elsewhere. */
33133274 s -> kaslr .virt_module_slots =
33143275 (s -> kaslr .virt_module_min && s -> kaslr .virt_module_max )
3315- ? quantity_slots (Q_MODULE_BASE , & g_auth_engine .est [Q_MODULE_BASE ],
3316- KASLD_SOUND_FLOOR , g_auth_engine .constraints ,
3317- g_auth_engine .n_constraints , PAGE_SIZE )
3276+ ? layout .virt_module_slots
33183277 : 0 ;
3319- #else
3320- {
3321- unsigned long a = (unsigned long )RANDOMIZE_MEMORY_ALIGN ;
3322- s -> kaslr .virt_page_offset_slots =
3323- (a && s -> kaslr .virt_page_offset_max > s -> kaslr .virt_page_offset_min )
3324- ? (s -> kaslr .virt_page_offset_max - s -> kaslr .virt_page_offset_min ) /
3325- a
3326- : 0 ;
3327- s -> kaslr .virt_vmalloc_slots =
3328- (a && s -> kaslr .virt_vmalloc_max > s -> kaslr .virt_vmalloc_min )
3329- ? (s -> kaslr .virt_vmalloc_max - s -> kaslr .virt_vmalloc_min ) / a
3330- : 0 ;
3331- s -> kaslr .virt_vmemmap_slots =
3332- (a && s -> kaslr .virt_vmemmap_max > s -> kaslr .virt_vmemmap_min )
3333- ? (s -> kaslr .virt_vmemmap_max - s -> kaslr .virt_vmemmap_min ) / a
3334- : 0 ;
3335- s -> kaslr .virt_module_slots =
3336- (s -> kaslr .virt_module_max > s -> kaslr .virt_module_min )
3337- ? (s -> kaslr .virt_module_max - s -> kaslr .virt_module_min ) / PAGE_SIZE
3338- : 0 ;
3339- }
3340- #endif
33413278 s -> kaslr .virt_page_offset_bits = s -> kaslr .virt_page_offset_slots > 0
33423279 ? ilog2 (s -> kaslr .virt_page_offset_slots )
33433280 : 0 ;
@@ -4358,6 +4295,45 @@ static void engine_sync_authoritative(const struct engine *e) {
43584295 layout .modules_end = obs_hi ;
43594296 }
43604297 }
4298+
4299+ /* Candidate counts, last: every window and alignment this reads is final by
4300+ * here, and the count must be taken from the same resolution as the window it
4301+ * counts. quantity_slots() is the only answer that carves the estimate's
4302+ * interior C_EXCLUDE holes; the alignments are the engine-resolved ones
4303+ * projected above, not the compile-time defaults.
4304+ *
4305+ * Counted unconditionally. Whether a count is worth PRESENTING is a question
4306+ * about the window's shape -- both edges known, and so on -- which the
4307+ * summary builder asks where those edges are in hand. Gating here instead
4308+ * would put a presentation rule in the projection and answer it from fields
4309+ * that do not exist yet. */
4310+ layout .virt_kaslr_slots = quantity_slots (
4311+ Q_VIRT_IMAGE_BASE , & e -> est [Q_VIRT_IMAGE_BASE ], KASLD_SOUND_FLOOR ,
4312+ e -> constraints , e -> n_constraints , layout .virt_kaslr_align );
4313+ /* Both image-base counts are taken here, on every arch. The phys window is
4314+ * projected under !TEXT_TRACKS_DIRECTMAP above because only a decoupled arch
4315+ * resolves it separately; the count is still meaningful on a coupled arch,
4316+ * where the estimate carries the locked-to-virt window. */
4317+ layout .phys_kaslr_slots = quantity_slots (
4318+ Q_PHYS_IMAGE_BASE , & e -> est [Q_PHYS_IMAGE_BASE ], KASLD_SOUND_FLOOR ,
4319+ e -> constraints , e -> n_constraints , layout .phys_kaslr_align );
4320+ layout .virt_page_offset_slots =
4321+ quantity_slots (Q_PAGE_OFFSET , & e -> est [Q_PAGE_OFFSET ], KASLD_SOUND_FLOOR ,
4322+ e -> constraints , e -> n_constraints , RANDOMIZE_MEMORY_ALIGN );
4323+ layout .virt_vmalloc_slots =
4324+ quantity_slots (Q_VMALLOC_BASE , & e -> est [Q_VMALLOC_BASE ], KASLD_SOUND_FLOOR ,
4325+ e -> constraints , e -> n_constraints , RANDOMIZE_MEMORY_ALIGN );
4326+ layout .virt_vmemmap_slots =
4327+ quantity_slots (Q_VMEMMAP_BASE , & e -> est [Q_VMEMMAP_BASE ], KASLD_SOUND_FLOOR ,
4328+ e -> constraints , e -> n_constraints , RANDOMIZE_MEMORY_ALIGN );
4329+ /* The module base is page-granular on every arch that randomizes it (x86_64
4330+ * draws a whole number of pages; the arm64 bounding box and the
4331+ * PAGE_OFFSET-derived bands are page-aligned), so PAGE_SIZE is the pitch
4332+ * rather than RANDOMIZE_MEMORY_ALIGN, which is an x86_64 memory-KASLR
4333+ * constant and 0 elsewhere. */
4334+ layout .virt_module_slots =
4335+ quantity_slots (Q_MODULE_BASE , & e -> est [Q_MODULE_BASE ], KASLD_SOUND_FLOOR ,
4336+ e -> constraints , e -> n_constraints , PAGE_SIZE );
43614337}
43624338#ifndef KASLD_TESTING
43634339
0 commit comments