Skip to content

Commit 607521e

Browse files
committed
Add KASLD_SECTION_BSS / KASLD_REGION_KERNEL_BSS vocabulary
Introduce two new type constants: KASLD_SECTION_BSS "bss" — kernel .bss virtual addresses KASLD_REGION_KERNEL_BSS "kernel_bss" — physical addresses of .bss-resident symbols KASLD_SECTION_DATA and KASLD_REGION_KERNEL_DATA now exclusively cover .data/.rodata; .bss is no longer included in their definition. Components: - dmesg_backtrace: re-tag CR3 results KERNEL_IMAGE → KERNEL_BSS. swapper_pg_dir lives in .bss; correct tagging lets inference plugins apply the BSS-resident gap refinement without a hardcoded name list. - dmesg_mem_init_kernel_layout: add ".bss : 0x" entry. ARM and x86_32 kernels print a .bss line in the same mem_init() layout block that the plugin already parses; it was previously silently skipped. Inference: - image_size_from_text_data_gap: include KASLD_SECTION_BSS in the max_data high-water mark. BSS addresses are inside the kernel image, so they give a valid (tighter) lower bound on image size. - min_offset_from_image_size (MIPS/LoongArch): same — gap used as image size lower bound, so BSS inclusion is sound. - riscv64_fdt_kaslr_seed: same — gap used as image size lower bound for nr_pos_max computation. All three gap-using plugins that include BSS are distinct from kernel_image_phys_bound.c's compute_virt_gap(), which must remain DATA-only because it bounds data_end_offset rather than image size. Render: add KASLD_SECTION_BSS and KASLD_REGION_KERNEL_BSS to all display-name functions and section_order[] arrays.
1 parent dd55fed commit 607521e

7 files changed

Lines changed: 64 additions & 48 deletions

File tree

src/components/dmesg_backtrace.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -213,15 +213,16 @@ int main(void) {
213213

214214
if (ctx.phys) {
215215
printf("leaked physical address (CR3): %lx\n", ctx.phys);
216-
/* CR3 is the page table base — physically the kernel image's
217-
* top-level pgd resides at this address (modulo PCID bits). */
216+
/* CR3 is the physical address of swapper_pg_dir, which lives in the
217+
* kernel .bss section. Tagged KERNEL_BSS so inference plugins can
218+
* apply the BSS-resident gap refinement without an allow-list. */
218219
kasld_result(KASLD_ADDR_PHYS, KASLD_SECTION_DRAM, ctx.phys,
219-
KASLD_REGION_KERNEL_IMAGE, "cr3");
220+
KASLD_REGION_KERNEL_BSS, "cr3");
220221
#if !PHYS_VIRT_DECOUPLED
221222
unsigned long virt = phys_to_virt(ctx.phys);
222223
printf("possible direct-map virtual address: %lx\n", virt);
223224
kasld_result(KASLD_ADDR_VIRT, KASLD_SECTION_DIRECTMAP, virt,
224-
KASLD_REGION_KERNEL_IMAGE, "cr3");
225+
KASLD_REGION_KERNEL_BSS, "cr3");
225226
#endif
226227
}
227228

src/components/dmesg_mem_init_kernel_layout.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// Sections extracted:
1010
// .text -> V text (kernel text virtual address)
1111
// .data -> V data (kernel data virtual address)
12+
// .bss -> V bss (kernel BSS virtual address; ARM and x86_32 only)
1213
// lowmem -> V directmap (lowmem / direct-mapped region, x86_32/arm)
1314
// modules -> V module (kernel module region, arm/arm64)
1415
// memory -> V directmap (linear memory map, arm64)
@@ -93,9 +94,9 @@
9394
KASLD_EXPLAIN(
9495
"Parses the kernel virtual memory layout block printed by mem_init() "
9596
"during boot. This block shows virtual address ranges for .text, "
96-
".data, lowmem, modules, and other sections. Removed from most "
97-
"architectures: ARM64 v4.16, ARM v5.1, x86_32 v5.7. Access is "
98-
"gated by dmesg_restrict.");
97+
".data, .bss (ARM and x86_32), lowmem, modules, and other sections. "
98+
"Removed from most architectures: ARM64 v4.16, ARM v5.1, x86_32 v5.7. "
99+
"Access is gated by dmesg_restrict.");
99100

100101
KASLD_META("method:parsed\n"
101102
"phase:inference\n"
@@ -128,6 +129,8 @@ static const struct layout_entry entries[] = {
128129
KASLD_REGION_KERNEL_TEXT, KERNEL_BASE_MIN, KERNEL_BASE_MAX},
129130
{".data : 0x", KASLD_ADDR_VIRT, KASLD_SECTION_DATA, "kernel .data start",
130131
KASLD_REGION_KERNEL_DATA, KERNEL_VAS_START, KERNEL_VAS_END},
132+
{".bss : 0x", KASLD_ADDR_VIRT, KASLD_SECTION_BSS, "kernel .bss start",
133+
KASLD_REGION_KERNEL_BSS, KERNEL_BASE_MIN, KERNEL_BASE_MAX},
131134
{"lowmem : 0x", KASLD_ADDR_VIRT, KASLD_SECTION_DIRECTMAP,
132135
"kernel lowmem start", KASLD_REGION_DIRECTMAP, KERNEL_VAS_START,
133136
KERNEL_VAS_END},

src/include/kasld.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,8 @@
239239
#define KASLD_SECTION_TEXT "text"
240240
#define KASLD_SECTION_MODULE "module"
241241
#define KASLD_SECTION_DIRECTMAP "directmap"
242-
#define KASLD_SECTION_DATA "data"
242+
#define KASLD_SECTION_DATA "data" /* kernel .data and .rodata; never .bss */
243+
#define KASLD_SECTION_BSS "bss" /* kernel .bss (zero-initialised data) */
243244
#define KASLD_SECTION_DRAM "dram"
244245
#define KASLD_SECTION_MMIO "mmio"
245246
#define KASLD_SECTION_PAGEOFFSET "pageoffset"
@@ -290,7 +291,8 @@
290291

291292
/* Kernel virtual memory regions. */
292293
#define KASLD_REGION_KERNEL_TEXT "kernel_text" /* kernel .text */
293-
#define KASLD_REGION_KERNEL_DATA "kernel_data" /* .data / .rodata / .bss */
294+
#define KASLD_REGION_KERNEL_DATA "kernel_data" /* kernel .data / .rodata */
295+
#define KASLD_REGION_KERNEL_BSS "kernel_bss" /* kernel .bss */
294296
#define KASLD_REGION_MODULE \
295297
"module" /* loaded LKM (instance: "module:<name>") */
296298
#define KASLD_REGION_MODULE_REGION \

src/inference/image_size_from_text_data_gap.c

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,18 @@
44
// (POST_COLLECTION)
55
//
66
// If the collected results contain at least one virtual TEXT address and at
7-
// least one virtual DATA address, their gap is a lower bound on the kernel
8-
// image size:
7+
// least one virtual DATA or BSS address, their gap is a lower bound on the
8+
// kernel image size:
99
//
10-
// image_size >= max(DATA results) - min(TEXT results)
10+
// image_size >= max(DATA or BSS results) - min(TEXT results)
1111
//
1212
// Soundness argument:
1313
// Let B = _stext (true kernel text base, unknown).
1414
// Any TEXT result T satisfies T >= B (kernel text is above _stext).
15-
// Any DATA result D satisfies D <= B + image_size (_end >= D).
16-
// Therefore: D - T <= (B + image_size) - B = image_size.
17-
// Equivalently: image_size >= D - T for any valid (T, D) pair.
18-
// Using max(DATA) - min(TEXT) maximises the lower bound from collected data.
15+
// Any DATA result D satisfies D <= B + image_size (_edata >= D).
16+
// Any BSS result X satisfies X <= B + image_size (_end >= X).
17+
// Therefore: max(D or X) - T <= image_size for any valid T.
18+
// Using max(DATA or BSS) - min(TEXT) maximises the lower bound.
1919
//
2020
// The kernel image must fit within the KASLR randomisation window:
2121
// text_base + image_size <= KASLR_BASE_MAX
@@ -30,11 +30,10 @@
3030
// the identical image size lower bound also tightens phys_base_max.
3131
//
3232
// Reliability notes:
33-
// - TEXT and DATA results are emitted by different components (backtrace,
34-
// dmesg_mem_init_kernel_layout, sysfs_iscsi_transport_handle, etc.) and
33+
// - TEXT, DATA, and BSS results are emitted by different components and
3534
// are always from the running kernel image, so they come from the same
3635
// single KASLR slot. The gap is always a sound lower bound within one run.
37-
// - If only TEXT or only DATA results are present, the plugin is a no-op.
36+
// - If only TEXT or only DATA/BSS results are present, the plugin is a no-op.
3837
// - Default ('D'-type) results are excluded implicitly: they carry type 'D',
3938
// not 'V', so they are skipped by the KASLD_ADDR_VIRT filter.
4039
//
@@ -65,7 +64,8 @@ static void image_size_from_text_data_gap_run(struct kasld_analysis_ctx *ctx) {
6564
if (strcmp(r->section, KASLD_SECTION_TEXT) == 0) {
6665
if (r->raw < min_text)
6766
min_text = r->raw;
68-
} else if (strcmp(r->section, KASLD_SECTION_DATA) == 0) {
67+
} else if (strcmp(r->section, KASLD_SECTION_DATA) == 0 ||
68+
strcmp(r->section, KASLD_SECTION_BSS) == 0) {
6969
if (r->raw > max_data)
7070
max_data = r->raw;
7171
}

src/inference/min_offset_from_image_size.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,13 @@
6363

6464
#if defined(__mips__) || defined(__loongarch__)
6565

66-
/* Compute kernel_length_estimate = max(DATA results) - min(TEXT results).
67-
* Returns 0 if insufficient results are present or the pair is inconsistent. */
66+
/* Compute kernel_length_estimate = max(DATA or BSS results) - min(TEXT
67+
* results). Returns 0 if insufficient results are present or the pair is
68+
* inconsistent. BSS addresses are included because they are inside the image
69+
* (BSS is past .rodata), making the estimate tighter. This is valid here
70+
* because the gap is used as a lower bound on kernel_length (image size), not
71+
* on data_end_offset — the distinction that makes kernel_image_phys_bound.c's
72+
* compute_virt_gap() intentionally DATA-only. */
6873
static unsigned long get_text_data_gap(const struct kasld_analysis_ctx *ctx) {
6974
unsigned long min_text = ULONG_MAX;
7075
unsigned long max_data = 0;
@@ -76,7 +81,8 @@ static unsigned long get_text_data_gap(const struct kasld_analysis_ctx *ctx) {
7681
if (strcmp(r->section, KASLD_SECTION_TEXT) == 0) {
7782
if (r->raw < min_text)
7883
min_text = r->raw;
79-
} else if (strcmp(r->section, KASLD_SECTION_DATA) == 0) {
84+
} else if (strcmp(r->section, KASLD_SECTION_DATA) == 0 ||
85+
strcmp(r->section, KASLD_SECTION_BSS) == 0) {
8086
if (r->raw > max_data)
8187
max_data = r->raw;
8288
}

src/inference/riscv64_fdt_kaslr_seed.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,12 @@ static unsigned long read_image_size(const char *release) {
150150

151151
/* Returns gap = max(DATA results) - min(TEXT results), a lower bound on
152152
* kernel_size. Returns 0 if insufficient results or inconsistent pair. */
153+
/* Compute image_size_lower_bound = max(DATA or BSS results) - min(TEXT
154+
* results). BSS is included because BSS addresses are inside the image; a
155+
* larger gap gives a tighter nr_pos_max and fewer valid candidate positions.
156+
* This is intentionally DATA+BSS (image size lower bound), unlike
157+
* kernel_image_phys_bound.c's compute_virt_gap() which is DATA-only because
158+
* it bounds data_end_offset rather than image size. */
153159
static unsigned long get_text_data_gap(const struct kasld_analysis_ctx *ctx) {
154160
unsigned long min_text = ULONG_MAX;
155161
unsigned long max_data = 0;
@@ -161,7 +167,8 @@ static unsigned long get_text_data_gap(const struct kasld_analysis_ctx *ctx) {
161167
if (strcmp(r->section, KASLD_SECTION_TEXT) == 0) {
162168
if (r->raw < min_text)
163169
min_text = r->raw;
164-
} else if (strcmp(r->section, KASLD_SECTION_DATA) == 0) {
170+
} else if (strcmp(r->section, KASLD_SECTION_DATA) == 0 ||
171+
strcmp(r->section, KASLD_SECTION_BSS) == 0) {
165172
if (r->raw > max_data)
166173
max_data = r->raw;
167174
}

src/render.c

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ static const char *section_display_name(char type, const char *section) {
4848
return "Direct map (virtual)";
4949
if (strcmp(section, KASLD_SECTION_DATA) == 0)
5050
return "Kernel data (virtual)";
51+
if (strcmp(section, KASLD_SECTION_BSS) == 0)
52+
return "Kernel BSS (virtual)";
5153
if (strcmp(section, KASLD_SECTION_DRAM) == 0)
5254
return "Physical DRAM";
5355
if (strcmp(section, KASLD_SECTION_MMIO) == 0)
@@ -68,7 +70,8 @@ static int is_kernel_locating_region(const char *region) {
6870
return 0;
6971
return strcmp(region, KASLD_REGION_KERNEL_IMAGE) == 0 ||
7072
strcmp(region, KASLD_REGION_KERNEL_TEXT) == 0 ||
71-
strcmp(region, KASLD_REGION_KERNEL_DATA) == 0;
73+
strcmp(region, KASLD_REGION_KERNEL_DATA) == 0 ||
74+
strcmp(region, KASLD_REGION_KERNEL_BSS) == 0;
7275
}
7376

7477
/* Display label for a kernel-locating region presented inline as its own
@@ -83,6 +86,8 @@ static const char *kernel_region_display_name(char type, const char *region) {
8386
return phys ? "Kernel text (physical)" : "Kernel text (virtual)";
8487
if (strcmp(region, KASLD_REGION_KERNEL_DATA) == 0)
8588
return phys ? "Kernel data (physical)" : "Kernel data (virtual)";
89+
if (strcmp(region, KASLD_REGION_KERNEL_BSS) == 0)
90+
return phys ? "Kernel BSS (physical)" : "Kernel BSS (virtual)";
8691
return NULL;
8792
}
8893

@@ -1651,13 +1656,10 @@ static void render_json(const struct summary *s) {
16511656
printf("\n },\n");
16521657

16531658
/* groups — build ordered list of unique (type, section) keys */
1654-
const char *section_order[] = {KASLD_SECTION_TEXT,
1655-
KASLD_SECTION_MODULE,
1656-
KASLD_SECTION_DIRECTMAP,
1657-
KASLD_SECTION_DATA,
1658-
KASLD_SECTION_DRAM,
1659-
KASLD_SECTION_MMIO,
1660-
NULL};
1659+
const char *section_order[] = {KASLD_SECTION_TEXT, KASLD_SECTION_MODULE,
1660+
KASLD_SECTION_DIRECTMAP, KASLD_SECTION_DATA,
1661+
KASLD_SECTION_BSS, KASLD_SECTION_DRAM,
1662+
KASLD_SECTION_MMIO, NULL};
16611663
char type_order[] = {KASLD_ADDR_VIRT, KASLD_ADDR_PHYS, 0};
16621664

16631665
struct group_key gkeys[64];
@@ -1899,13 +1901,10 @@ static void render_text(const struct summary *s) {
18991901
}
19001902

19011903
/* Print each (type, section) group in a defined order */
1902-
const char *section_order[] = {KASLD_SECTION_TEXT,
1903-
KASLD_SECTION_MODULE,
1904-
KASLD_SECTION_DIRECTMAP,
1905-
KASLD_SECTION_DATA,
1906-
KASLD_SECTION_DRAM,
1907-
KASLD_SECTION_MMIO,
1908-
NULL};
1904+
const char *section_order[] = {KASLD_SECTION_TEXT, KASLD_SECTION_MODULE,
1905+
KASLD_SECTION_DIRECTMAP, KASLD_SECTION_DATA,
1906+
KASLD_SECTION_BSS, KASLD_SECTION_DRAM,
1907+
KASLD_SECTION_MMIO, NULL};
19091908
char type_order[] = {KASLD_ADDR_VIRT, KASLD_ADDR_PHYS, 0};
19101909

19111910
if (verbose) {
@@ -1971,8 +1970,9 @@ static void render_text(const struct summary *s) {
19711970
} else {
19721971
/* Compact: one line per (type, section) for non-kernel-locating
19731972
* regions, plus one line per kernel-locating region (kernel_image,
1974-
* kernel_text, kernel_data) so direct kernel-base disclosures are not
1975-
* buried inside a generic "Physical DRAM" / "Physical MMIO" range. */
1973+
* kernel_text, kernel_data, kernel_bss) so direct kernel-base
1974+
* disclosures are not buried inside a generic "Physical DRAM" /
1975+
* "Physical MMIO" range. */
19761976
for (int t = 0; type_order[t]; t++) {
19771977
for (int si = 0; section_order[si]; si++) {
19781978
if (group_already_printed(type_order[t], section_order[si]))
@@ -2173,13 +2173,10 @@ static void render_markdown(const struct summary *s) {
21732173
}
21742174

21752175
/* Result groups */
2176-
const char *section_order[] = {KASLD_SECTION_TEXT,
2177-
KASLD_SECTION_MODULE,
2178-
KASLD_SECTION_DIRECTMAP,
2179-
KASLD_SECTION_DATA,
2180-
KASLD_SECTION_DRAM,
2181-
KASLD_SECTION_MMIO,
2182-
NULL};
2176+
const char *section_order[] = {KASLD_SECTION_TEXT, KASLD_SECTION_MODULE,
2177+
KASLD_SECTION_DIRECTMAP, KASLD_SECTION_DATA,
2178+
KASLD_SECTION_BSS, KASLD_SECTION_DRAM,
2179+
KASLD_SECTION_MMIO, NULL};
21832180
char type_order[] = {KASLD_ADDR_PHYS, KASLD_ADDR_VIRT, 0};
21842181

21852182
printf("## Leak Results\n\n");

0 commit comments

Comments
 (0)