|
11 | 11 | #include "../src/inference/dram_bound.c" |
12 | 12 | #include "../src/inference/image_size_from_text_data_gap.c" |
13 | 13 | #include "../src/inference/kaslr_ceiling.c" |
| 14 | +#include "../src/inference/kernel_image_phys_bound.c" |
14 | 15 | #include "../src/inference/layout_adjust.c" |
15 | 16 | #include "../src/inference/module_text_bound.c" |
16 | 17 | #include "../src/inference/phys_virt_synth.c" |
@@ -2343,6 +2344,138 @@ static void test_randomize_memory_non_ram_base_witness_noop(void) { |
2343 | 2344 | assert(g_ctx.page_offset_max == po_max); |
2344 | 2345 | } |
2345 | 2346 |
|
| 2347 | +/* ========================================================================= |
| 2348 | + * kernel_image_phys_bound (POST_COLLECTION) |
| 2349 | + * Bounds phys_base from kernel-locating PHYS witnesses; BSS-resident |
| 2350 | + * witnesses (e.g. cr3) refine the upper bound using virt TEXT/DATA gap. |
| 2351 | + * ========================================================================= |
| 2352 | + */ |
| 2353 | + |
| 2354 | +#define KIPB_MAX_IMAGE_SIZE (256ul * 1024 * 1024) |
| 2355 | + |
| 2356 | +/* Witnessless: no kernel-locating PHYS results → no change. */ |
| 2357 | +static void test_kernel_image_phys_no_witness_noop(void) { |
| 2358 | + reset_state(); |
| 2359 | + /* Non-kernel-locating PHYS witnesses must be ignored. */ |
| 2360 | + inject_result(KASLD_ADDR_PHYS, KASLD_SECTION_DRAM, 0x1000, |
| 2361 | + KASLD_REGION_RAM_BASE); |
| 2362 | + inject_result(KASLD_ADDR_PHYS, KASLD_SECTION_DRAM, 0x100000000ul, |
| 2363 | + KASLD_REGION_RAM_TOP); |
| 2364 | + init_inference_ctx(); |
| 2365 | + unsigned long min0 = g_ctx.phys_base_min; |
| 2366 | + unsigned long max0 = g_ctx.phys_base_max; |
| 2367 | + run_inference_phase(&g_ctx, KASLD_INFER_PHASE_POST_COLLECTION); |
| 2368 | + assert(g_ctx.phys_base_min == min0); |
| 2369 | + assert(g_ctx.phys_base_max == max0); |
| 2370 | +} |
| 2371 | + |
| 2372 | +/* Note on test setup: every kernel_image_phys_bound test that expects |
| 2373 | + * tightening also injects a low PHYS/DRAM `ram_base` witness. This pins |
| 2374 | + * dram_bound's `min PHYS/DRAM` to the low value, causing dram_bound's |
| 2375 | + * tightening check to fail (it can't raise phys_base_min above |
| 2376 | + * `phys_kaslr_base_min`). Without this, dram_bound would raise |
| 2377 | + * phys_base_min above the kernel_image witness in its same convergence |
| 2378 | + * pass, blocking kernel_image_phys_bound from firing — an order-dependence |
| 2379 | + * that doesn't occur in production parallel mode (where ram_base witnesses |
| 2380 | + * from /sys/firmware/memmap or /proc/zoneinfo are present alongside cr3). |
| 2381 | + */ |
| 2382 | +#define KIPB_RAM_BASE (0x1000ul) |
| 2383 | + |
| 2384 | +/* Single kernel_image witness → both bounds tighten symmetrically around |
| 2385 | + * the witness, with the lower bound separated by MAX_IMAGE_SIZE. */ |
| 2386 | +static void test_kernel_image_phys_single_witness_tightens(void) { |
| 2387 | + reset_state(); |
| 2388 | + unsigned long cr3 = 0x119446000ul; |
| 2389 | + inject_result(KASLD_ADDR_PHYS, KASLD_SECTION_DRAM, KIPB_RAM_BASE, |
| 2390 | + KASLD_REGION_RAM_BASE); |
| 2391 | + inject_result(KASLD_ADDR_PHYS, KASLD_SECTION_DRAM, cr3, "kernel_bss:cr3"); |
| 2392 | + init_inference_ctx(); |
| 2393 | + run_inference_phase(&g_ctx, KASLD_INFER_PHASE_POST_COLLECTION); |
| 2394 | + /* phys_base_max ≤ cr3, aligned down to phys_kaslr_align (2 MiB). */ |
| 2395 | + unsigned long align = g_ctx.arch->phys_kaslr_align; |
| 2396 | + assert(g_ctx.phys_base_max <= cr3); |
| 2397 | + assert((g_ctx.phys_base_max & (align - 1)) == 0); |
| 2398 | + /* phys_base_min ≥ cr3 - MAX_IMAGE_SIZE + 1, aligned up. */ |
| 2399 | + unsigned long expected_min = |
| 2400 | + (cr3 - KIPB_MAX_IMAGE_SIZE + 1 + align - 1) & ~(align - 1); |
| 2401 | + assert(g_ctx.phys_base_min == expected_min); |
| 2402 | +} |
| 2403 | + |
| 2404 | +/* BSS-resident refinement: cr3 witness *plus* a VIRT/TEXT and VIRT/DATA |
| 2405 | + * pair → phys_base_max contribution is `cr3 - virt_gap` (tighter than |
| 2406 | + * cr3 alone). */ |
| 2407 | +static void test_kernel_image_phys_bss_refinement_with_gap(void) { |
| 2408 | + reset_state(); |
| 2409 | + unsigned long cr3 = 0x119446000ul; |
| 2410 | + unsigned long vtext_min = KERNEL_BASE_MIN + 0x1000000ul; /* +16 MiB */ |
| 2411 | + /* Place max(VIRT DATA) such that gap = 32 MiB (well within image). */ |
| 2412 | + unsigned long gap = 32ul * 1024 * 1024; |
| 2413 | + unsigned long vdata_max = vtext_min + gap; |
| 2414 | + |
| 2415 | + inject_result(KASLD_ADDR_PHYS, KASLD_SECTION_DRAM, KIPB_RAM_BASE, |
| 2416 | + KASLD_REGION_RAM_BASE); |
| 2417 | + inject_result(KASLD_ADDR_VIRT, KASLD_SECTION_TEXT, vtext_min, |
| 2418 | + KASLD_REGION_KERNEL_TEXT); |
| 2419 | + inject_result(KASLD_ADDR_VIRT, KASLD_SECTION_DATA, vdata_max, |
| 2420 | + KASLD_REGION_KERNEL_DATA); |
| 2421 | + inject_result(KASLD_ADDR_PHYS, KASLD_SECTION_DRAM, cr3, "kernel_bss:cr3"); |
| 2422 | + init_inference_ctx(); |
| 2423 | + run_inference_phase(&g_ctx, KASLD_INFER_PHASE_POST_COLLECTION); |
| 2424 | + /* phys_base_max ≤ cr3 - gap (BSS-resident refinement), aligned down. */ |
| 2425 | + unsigned long align = g_ctx.arch->phys_kaslr_align; |
| 2426 | + unsigned long expected_max = (cr3 - gap) & ~(align - 1); |
| 2427 | + assert(g_ctx.phys_base_max == expected_max); |
| 2428 | +} |
| 2429 | + |
| 2430 | +/* Non-cr3 kernel_image witness (no name match) → BSS refinement does NOT |
| 2431 | + * apply, even with a virt gap present. */ |
| 2432 | +static void test_kernel_image_phys_no_bss_refinement_for_unknown_name(void) { |
| 2433 | + reset_state(); |
| 2434 | + unsigned long w = 0x119446000ul; |
| 2435 | + unsigned long vtext_min = KERNEL_BASE_MIN + 0x1000000ul; |
| 2436 | + unsigned long gap = 32ul * 1024 * 1024; |
| 2437 | + unsigned long vdata_max = vtext_min + gap; |
| 2438 | + inject_result(KASLD_ADDR_PHYS, KASLD_SECTION_DRAM, KIPB_RAM_BASE, |
| 2439 | + KASLD_REGION_RAM_BASE); |
| 2440 | + inject_result(KASLD_ADDR_VIRT, KASLD_SECTION_TEXT, vtext_min, |
| 2441 | + KASLD_REGION_KERNEL_TEXT); |
| 2442 | + inject_result(KASLD_ADDR_VIRT, KASLD_SECTION_DATA, vdata_max, |
| 2443 | + KASLD_REGION_KERNEL_DATA); |
| 2444 | + /* Region kernel_image (not kernel_bss) → BSS refinement does NOT apply |
| 2445 | + * regardless of name. The discriminant is the region tag, not the name. */ |
| 2446 | + inject_result(KASLD_ADDR_PHYS, KASLD_SECTION_DRAM, w, |
| 2447 | + "kernel_image:unknown_symbol"); |
| 2448 | + init_inference_ctx(); |
| 2449 | + run_inference_phase(&g_ctx, KASLD_INFER_PHASE_POST_COLLECTION); |
| 2450 | + /* phys_base_max ≤ W (no gap subtraction), aligned down. */ |
| 2451 | + unsigned long align = g_ctx.arch->phys_kaslr_align; |
| 2452 | + assert(g_ctx.phys_base_max == (w & ~(align - 1))); |
| 2453 | +} |
| 2454 | + |
| 2455 | +/* Conflicting witnesses (spread > MAX_IMAGE_SIZE) → skip entirely. */ |
| 2456 | +static void test_kernel_image_phys_contradictory_witnesses_noop(void) { |
| 2457 | + reset_state(); |
| 2458 | + unsigned long lo_w = 0x119446000ul; |
| 2459 | + unsigned long hi_w = lo_w + KIPB_MAX_IMAGE_SIZE + 0x100000ul; /* +1 MiB */ |
| 2460 | + inject_result(KASLD_ADDR_PHYS, KASLD_SECTION_DRAM, KIPB_RAM_BASE, |
| 2461 | + KASLD_REGION_RAM_BASE); |
| 2462 | + inject_result(KASLD_ADDR_PHYS, KASLD_SECTION_DRAM, lo_w, "kernel_bss:cr3"); |
| 2463 | + inject_result(KASLD_ADDR_PHYS, KASLD_SECTION_DRAM, hi_w, |
| 2464 | + KASLD_REGION_KERNEL_IMAGE); |
| 2465 | + init_inference_ctx(); |
| 2466 | + unsigned long max0 = g_ctx.phys_base_max; |
| 2467 | + run_inference_phase(&g_ctx, KASLD_INFER_PHASE_POST_COLLECTION); |
| 2468 | + /* The conflict guard refused to emit any bound from this plugin. With |
| 2469 | + * the spread exceeding MAX_IMAGE_SIZE, the plugin returns before |
| 2470 | + * computing new_phys_max. The rest of the convergence loop may still |
| 2471 | + * shift things but the plugin under test must not have pinned |
| 2472 | + * phys_base_max to lo_w. */ |
| 2473 | + assert(g_ctx.phys_base_max > lo_w || g_ctx.phys_base_max == max0); |
| 2474 | +} |
| 2475 | + |
| 2476 | +#undef KIPB_RAM_BASE |
| 2477 | +#undef KIPB_MAX_IMAGE_SIZE |
| 2478 | + |
2346 | 2479 | #endif /* __x86_64__ */ |
2347 | 2480 |
|
2348 | 2481 | /* ========================================================================= |
@@ -2698,6 +2831,14 @@ int main(void) { |
2698 | 2831 | RUN_TEST(test_randomize_memory_unaligned_candidate_noop); |
2699 | 2832 | RUN_TEST(test_randomize_memory_non_ram_base_witness_noop); |
2700 | 2833 | printf("\n"); |
| 2834 | + |
| 2835 | + printf("inference plugins (kernel_image_phys_bound):\n"); |
| 2836 | + RUN_TEST(test_kernel_image_phys_no_witness_noop); |
| 2837 | + RUN_TEST(test_kernel_image_phys_single_witness_tightens); |
| 2838 | + RUN_TEST(test_kernel_image_phys_bss_refinement_with_gap); |
| 2839 | + RUN_TEST(test_kernel_image_phys_no_bss_refinement_for_unknown_name); |
| 2840 | + RUN_TEST(test_kernel_image_phys_contradictory_witnesses_noop); |
| 2841 | + printf("\n"); |
2701 | 2842 | #endif |
2702 | 2843 |
|
2703 | 2844 | #if defined(__aarch64__) |
|
0 commit comments