Skip to content

Commit 27f4722

Browse files
committed
output: correct window counts, grade the readout, fix the progress bar
Window candidate counts read the closed [lo, hi] range as half-open, so a count never reconciled with the edges printed beside it, a pin reported zero candidates, and vslot_valid rejected a base sitting exactly on virt_kaslr_text_max -- the value the readout prints as the likely answer. Count both edges. The readout grades what it reports: "narrowed" replaces "not derandomized" above rows that do show narrowing, every window row states its grade rather than only those a likely row contrasts, and residual entropy is stated against what the KASLR window started with (~5 of 9 bits), in json and markdown too. Leak rows disclose interior-only groups and their contributing components. The progress bar owns its terminal line: diagnostics erase and repaint it instead of concatenating onto it, both perror paths gain the missing quiet gate, and the bar is erased on the stream it was drawn on rather than walked off by newlines on stdout. Killed components are reported in the readout, not only in narration the bar scrolls past.
1 parent a61df2d commit 27f4722

13 files changed

Lines changed: 730 additions & 182 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ The default text mode prints an answer-first overview:
7575
KASLD 0.3.1-dev -- Kernel ASLR derandomization
7676
Target: x86_64 / 6.15.6
7777
78-
Running 94 components (3 experimental skipped; use -x to enable)...
78+
Running 94 of 97 components (3 experimental skipped; use -x to enable)...
7979
[####################] 100% 94/94 13.9s
8080
8181
Virtual image base 0xffffffff8fe00000 slide +0xee00000

docs/architecture.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ raise the floor (DRAM bounds) and the 2 MiB `IMAGE_ALIGN` slot grid is applied.
9898
With only this one leak the result is still a window — several slots wide:
9999

100100
```
101-
Virtual image base not derandomized ~3 bits
102-
0xffffffff81000000 - 0xffffffff81f41000 (7 x 2.0 MiB)
101+
Virtual image base narrowed ~3 of 9 bits
102+
0xffffffff81000000 - 0xffffffff81f41000 guaranteed (8 x 2.0 MiB)
103103
```
104104

105105
A second observation — a `_stext` base witness, a DRAM floor, or the

src/estimate.c

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -494,14 +494,17 @@ unsigned long quantity_slots(enum kasld_quantity q, const struct estimate *e,
494494
continue;
495495
slots += (rs[i].hi - first) / e->stride + 1;
496496
} else {
497-
/* Whole-slot span. A non-empty range narrower than one slot still
498-
* occupies one slot (the base sits inside it) — so it counts as 1
499-
* candidate (0 bits), not 0. Without this a sub-slot window would report
500-
* "0 slots" and be indistinguishable from an empty result. */
501-
unsigned long w = (rs[i].hi - rs[i].lo) / step;
502-
if (w == 0 && rs[i].hi > rs[i].lo)
503-
w = 1;
504-
slots += w;
497+
/* Whole-slot pitch over the closed range [lo, hi]: the `+ 1` counts the
498+
* floor itself, so a pin (lo == hi) is one candidate and a range
499+
* narrower than one slot is one candidate rather than none.
500+
*
501+
* A carved hole can leave a sub-range whose floor is off the slot grid,
502+
* in which case this counts one placement more than exist. That
503+
* direction is deliberate: the count feeds the residual-entropy
504+
* headline, where over-stating what survives is the conservative error
505+
* and under-stating it would claim more of KASLR is defeated than the
506+
* evidence shows. */
507+
slots += (rs[i].hi - rs[i].lo) / step + 1;
505508
}
506509
}
507510
return slots;

src/include/kasld/estimate.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,11 @@ int quantity_ranges(enum kasld_quantity q, const struct estimate *e,
139139
int n_cs, struct range *out, int out_max);
140140

141141
/* Hole-aware count of aligned candidate positions for q's resolved estimate:
142-
* the sum over quantity_ranges() of (span / align), span = hi - lo per range.
143-
* With no excludes this is (hi - lo) / align; each interior C_EXCLUDE hole at
144-
* conf >= floor strictly reduces it. Returns 0 for align == 0 or a non-interval
142+
* the sum over quantity_ranges() of the candidates in each closed range,
143+
* (hi - lo) / align + 1. The ranges are inclusive of both edges (see `struct
144+
* range` above), so the trailing + 1 counts the floor: a pinned quantity is one
145+
* candidate, not none. Each interior C_EXCLUDE hole at conf >= floor strictly
146+
* reduces the total. Returns 0 for align == 0 or a non-interval
145147
* lattice. `floor` has the same meaning and requirement as in quantity_ranges:
146148
* pass the floor `e` was resolved at. This is the terminal consumer over the
147149
* range iterator above — the basis for hole-aware slot/entropy reporting, and

src/include/kasld/internal.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,18 @@ struct kaslr_info {
407407
unsigned long pslots;
408408
int pbits;
409409
int has_phys;
410+
/* Entropy the virtual KASLR window started with, counted over
411+
* Q_VIRT_IMAGE_BASE's honest top at the same alignment as vbits. The
412+
* baseline that makes vbits interpretable: "5 bits remain" says nothing
413+
* without it. 0 means not computed, and every format then reports the
414+
* residual alone.
415+
*
416+
* Virtual only, deliberately. That quantity's honest top IS the arch's
417+
* KASLR window, so the ratio means what a reader will take it to mean. The
418+
* physical and direct-map tops are addressable-range bounds rather than
419+
* randomization windows, so a denominator drawn from them would read as
420+
* KASLR entropy the kernel never had. */
421+
int vbits_top;
410422
/* Speculative "likely" window: the engine resolved a second time with ALL
411423
* signals, including those below the sound floor (timing/heuristic/brute).
412424
* It is a subset of the guaranteed window in the vtext/ptext fields above and

src/include/kasld/render_internal.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,14 @@ unsigned long section_consensus(enum kasld_addr_type type, const char *section,
5252
void section_consensus_info(enum kasld_addr_type type, const char *section,
5353
enum kasld_region region_filter,
5454
const char **best_method, int *n_sources,
55-
int *n_conflicts);
55+
int *n_conflicts, int *interior_only);
56+
57+
/* Distinct contributing components, and "no edge record, only interior samples"
58+
* predicate, for a (type, section, optional region_filter). */
59+
int section_source_count(enum kasld_addr_type type, const char *section,
60+
enum kasld_region region_filter);
61+
int section_is_interior_only(enum kasld_addr_type type, const char *section,
62+
enum kasld_region region_filter);
5663

5764
/* (type, section) extent across all in-bounds results. lo/hi = 0 when none. */
5865
void section_range(enum kasld_addr_type type, const char *section,

0 commit comments

Comments
 (0)