Skip to content

Commit 5971127

Browse files
committed
components: make the firmware_memmap RAM covering all-or-nothing
firmware_memmap emits a COMPLETE RAM covering; firmware_memmap_holes and ram_map_phys_exclude carve the gaps between extents as forbidden bands. But kasld_load_ram_extents silently dropped entries on overflow (max=64), on a failed parse of a System RAM entry, or on i386/PAE >4 GiB truncation (strtoul into unsigned long) — each fabricating a false gap that becomes an unsound C_EXCLUDE on the phys base. Make the loader all-or-nothing: parse with strtoull + a word-fit check, and return -1 (caller emits nothing) on any overflow / parse failure / truncation — mirroring boot_params_e820's covering_ok guard. Bump the cap to 128.
1 parent fdc2e29 commit 5971127

2 files changed

Lines changed: 61 additions & 22 deletions

File tree

src/components/firmware_memmap.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,13 @@ KASLD_META("method:parsed\n"
2121
"phase:inference\n");
2222

2323
int main(void) {
24-
struct kasld_ram_extent ext[64];
25-
int n = kasld_load_ram_extents(ext, 64);
24+
struct kasld_ram_extent ext[128];
25+
int n = kasld_load_ram_extents(ext, 128);
26+
/* n < 0: the map could not be captured completely (overflow, parse failure,
27+
* or word truncation). A partial covering would fabricate false gaps that
28+
* gap-carving rules turn into unsound exclusions, so emit nothing. */
29+
if (n < 0)
30+
return 0;
2631
for (int i = 0; i < n; i++)
2732
kasld_result_extent(KASLD_TYPE_PHYS, REGION_RAM, ext[i].lo, ext[i].hi, NULL,
2833
CONF_PARSED);

src/include/kasld/firmware_memmap.h

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
// fields via kasld_fopen), so they honour KASLD_SYSROOT redirection.
99
//
1010
// Read by the engine bridge, which emits these as PHYS RAM extents that the
11-
// firmware_memmap_holes verdict consumes.
11+
// firmware_memmap_holes verdict consumes. The loader is all-or-nothing: it
12+
// returns -1 (caller emits nothing) if the map cannot be captured completely,
13+
// because a partial covering would fabricate false gaps for gap-carving.
1214
// ---
1315
// <bcoles@gmail.com>
1416

@@ -42,42 +44,74 @@ kasld_memmap_first_line(const char *path, char *buf, size_t len) {
4244
return 0;
4345
}
4446

45-
/* Collect "System RAM" inclusive extents; returns count (0 if absent). */
47+
/* Collect "System RAM" inclusive extents into out[0..max).
48+
*
49+
* Returns the number of extents, or -1 if the firmware map cannot be
50+
* represented COMPLETELY and faithfully: more than `max` System RAM entries,
51+
* a numbered entry whose type/start/end could not be read or parsed, or an
52+
* extent value that does not fit unsigned long (i386/PAE >4 GiB truncation).
53+
* A covering MUST be complete — a truncated or partial map fabricates false
54+
* gaps that the gap-carving rules (firmware_memmap_holes, ram_map_phys_exclude)
55+
* turn into unsound C_EXCLUDEs — so callers MUST emit nothing on -1. Mirrors
56+
* the all-or-nothing covering_ok guard in boot_params_e820. */
4657
__attribute__((unused)) static int
4758
kasld_load_ram_extents(struct kasld_ram_extent *out, int max) {
4859
DIR *d = kasld_opendir(KASLD_MEMMAP_BASE);
4960
if (!d)
5061
return 0;
51-
int n = 0;
62+
int n = 0, incomplete = 0;
5263
struct dirent *ent;
53-
while ((ent = readdir(d)) != NULL && n < max) {
64+
while ((ent = readdir(d)) != NULL) {
5465
if (ent->d_name[0] == '.')
5566
continue;
5667
char path[512], buf[256];
5768
snprintf(path, sizeof(path), "%s/%s/type", KASLD_MEMMAP_BASE, ent->d_name);
58-
if (kasld_memmap_first_line(path, buf, sizeof(buf)) != 0)
59-
continue;
69+
if (kasld_memmap_first_line(path, buf, sizeof(buf)) != 0) {
70+
incomplete = 1; /* numbered entry with no type: map state unknown */
71+
break;
72+
}
6073
if (strcmp(buf, "System RAM") != 0)
61-
continue;
74+
continue; /* a genuine non-RAM region: a real gap, correctly omitted */
75+
/* From here the entry IS System RAM; any failure makes the map partial. */
76+
if (n >= max) {
77+
incomplete = 1; /* more RAM entries than out[]: would truncate the map */
78+
break;
79+
}
6280
snprintf(path, sizeof(path), "%s/%s/start", KASLD_MEMMAP_BASE, ent->d_name);
63-
if (kasld_memmap_first_line(path, buf, sizeof(buf)) != 0)
64-
continue;
81+
if (kasld_memmap_first_line(path, buf, sizeof(buf)) != 0) {
82+
incomplete = 1;
83+
break;
84+
}
6585
char *endp;
66-
unsigned long start = strtoul(buf, &endp, 16);
67-
if (endp == buf)
68-
continue;
86+
unsigned long long start = strtoull(buf, &endp, 16);
87+
if (endp == buf) {
88+
incomplete = 1;
89+
break;
90+
}
6991
snprintf(path, sizeof(path), "%s/%s/end", KASLD_MEMMAP_BASE, ent->d_name);
70-
if (kasld_memmap_first_line(path, buf, sizeof(buf)) != 0)
71-
continue;
72-
unsigned long end = strtoul(buf, &endp, 16);
73-
if (endp == buf || end < start)
74-
continue;
75-
out[n].lo = start;
76-
out[n].hi = end;
92+
if (kasld_memmap_first_line(path, buf, sizeof(buf)) != 0) {
93+
incomplete = 1;
94+
break;
95+
}
96+
unsigned long long end = strtoull(buf, &endp, 16);
97+
if (endp == buf || end < start) {
98+
incomplete = 1;
99+
break;
100+
}
101+
/* On i386 unsigned long is 32-bit; a >4 GiB extent would truncate and
102+
* corrupt the covering. Suppress the whole map rather than store a wrong
103+
* extent. */
104+
if ((unsigned long long)(unsigned long)start != start ||
105+
(unsigned long long)(unsigned long)end != end) {
106+
incomplete = 1;
107+
break;
108+
}
109+
out[n].lo = (unsigned long)start;
110+
out[n].hi = (unsigned long)end;
77111
n++;
78112
}
79113
closedir(d);
80-
return n;
114+
return incomplete ? -1 : n;
81115
}
82116

83117
#endif /* KASLD_FIRMWARE_MEMMAP_H */

0 commit comments

Comments
 (0)