Skip to content

Commit ccf036a

Browse files
committed
rules: keep the mips/loongarch image-size bound out of the sound window
min_offset_from_image_size emits virt_image_base >= floor + image span, because the placer bumps a slide that would land inside the image past it. That holds only if the placer ran. arch/mips/kernel/relocate.c and arch/loongarch/kernel/relocate.c both return the link address untouched under kaslr_disabled(), and the no-KASLR base sits at the window floor on loongarch, 0x100000 + IMAGE_BASE_OFFSET above it on mips — either way far below floor plus a whole image. With KASLR off the bound excluded the truth, at CONF_INFERRED, which is the sound floor. The premise cannot be established from a leak. There is no positive "KASLR ran" fact: absence of SF_VIRT_KASLR_DISABLED means the disable was not observed, and a vantage with no readable cmdline or config cannot tell that from KASLR being on. The lowest text observation does not settle it either, since a text symbol sits above the base. So the claim moves below the floor, to CONF_HEURISTIC: it shapes the speculative result and can no longer narrow the guaranteed window. An observed disable skips the rule outright, so a claim already known false is not made even speculatively. No live exposure — the rule needs a VIRT kernel_data/bss observation and nothing emits one on these arches, which is why boot-nokaslr.log resolves soundly today. The cost is real: mips and loongarch lose a guaranteed-window narrowing that was never sound to make.
1 parent fc5c00f commit ccf036a

2 files changed

Lines changed: 31 additions & 1 deletion

File tree

src/rules/min_offset_from_image_size.c

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,25 @@
1212
// C_LOWER_BOUND on Q_VIRT_IMAGE_BASE.
1313
// MIPS/LoongArch only; inert when no VIRT TEXT/IMAGE or DATA/BSS observation
1414
// is present.
15+
//
16+
// The bound holds only if the placer ran. arch/mips/kernel/relocate.c and
17+
// arch/loongarch/kernel/relocate.c both return the link address untouched when
18+
// kaslr_disabled(), leaving offset 0 -- and the no-KASLR base sits at the
19+
// window floor (loongarch) or just above it (mips, by 0x100000 +
20+
// IMAGE_BASE_OFFSET), either way far below floor + a whole image. So with
21+
// KASLR off the bound excludes the truth.
22+
//
23+
// That premise cannot be established from a leak. A positive "KASLR ran" fact
24+
// does not exist: the absence of SF_VIRT_KASLR_DISABLED means the disable was
25+
// not observed, not that it did not happen, and a vantage with no readable
26+
// cmdline or config cannot tell the two apart. The lowest text observation does
27+
// not settle it either -- a text symbol sits above the base, so finding one
28+
// above the compile-time default proves nothing about the base.
29+
//
30+
// Hence CONF_HEURISTIC, below the sound floor: this shapes the speculative
31+
// result only and can never narrow the guaranteed window. A positive disable
32+
// signal skips it outright, so a claim already known to be false is not made
33+
// even speculatively.
1534
// ---
1635
// <bcoles@gmail.com>
1736

@@ -29,6 +48,14 @@ int rule_min_offset_from_image_size(const struct evidence_set *ev,
2948
if (out_max < 1)
3049
return 0;
3150

51+
for (int i = 0; i < ev->n_obs; i++) {
52+
const struct observation *o = &ev->obs[i];
53+
if (!o->valid || o->value_kind != OBS_SCALAR)
54+
continue;
55+
if (o->scalar_fact == SF_VIRT_KASLR_DISABLED && o->scalar_value != 0)
56+
return 0;
57+
}
58+
3259
unsigned long min_text = ULONG_MAX, max_data = 0;
3360
uint32_t tsrc = 0, dsrc = 0;
3461
for (int i = 0; i < ev->n_obs; i++) {
@@ -64,7 +91,7 @@ int rule_min_offset_from_image_size(const struct evidence_set *ev,
6491
c->q = Q_VIRT_IMAGE_BASE;
6592
c->op = C_LOWER_BOUND;
6693
c->value = new_min;
67-
c->conf = CONF_INFERRED;
94+
c->conf = CONF_HEURISTIC;
6895
c->derived_from[0] = tsrc;
6996
c->derived_from[1] = dsrc;
7097
c->lineage_count = 2;

tests/vm/init.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,9 @@ int main(void) {
548548
mount("sysfs", "/sys", "sysfs", 0, "");
549549
mount("devtmpfs", "/dev", "devtmpfs", 0, "");
550550
mount("tmpfs", "/tmp", "tmpfs", 0, "");
551+
/* debugfs, the source for debugfs-based leak components. Mounts only where
552+
* CONFIG_DEBUG_FS is built in; the call fails harmlessly otherwise. */
553+
mount("debugfs", "/sys/kernel/debug", "debugfs", 0, "");
551554

552555
/* Before anything reads /proc/modules or /sys/module. */
553556
load_staged_modules();

0 commit comments

Comments
 (0)