l32_fixlabel partition loop lacks internal d_npartitions cap
| Field | Value |
|---|---|
| ID | DF-0109 |
| Status | new |
| Severity | Info |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:N |
| CWE | CWE-129 Improper Validation of Array Index |
| File | sys/kern/subr_disklabel32.c |
| Lines | 592-614 |
| Area | kern |
| Confidence | low |
| Discovered | 2026-06-30 |
| Reported | pending |
Summary
l32_fixlabel iterates lp->d_npartitions times over the fixed-size
d_partitions[] array (:592) with no internal cap. Currently gated
by the dkcksum32 OOB bug (DF-0106/DF-0107), but as defense-in-depth
the function should not trust its caller.
Root cause
l32_fixlabel at :592:
for (part = 0; part < lp->d_npartitions; part++, pp++) {
if (pp->p_offset != 0 || pp->p_size != 0) {
...
bzero(pp, sizeof *pp); // line 609
pp->p_offset += offset; // line 611
}
}
pp starts at &d_partitions[RAW_PART] (:590) and increments. If
d_npartitions > MAXPARTITIONS32, pp walks past the array, and
bzero(pp, sizeof *pp) corrupts memory past the struct.
Currently this is only reachable if dkcksum32 returned 0 on a label
with d_npartitions > MAXPARTITIONS32 — which requires the OOB bytes to
XOR to 0 against an attacker-chosen checksum. In the writedisklabel
path, *dlp = *lp at :365 replaces the on-disk label with the new
label before fixlabel runs, so d_npartitions is the new label's value
(typically 16). The risk is theoretical.
Threat model & preconditions
- Attacker position: N/A — defense-in-depth.
- Impact: Would allow OOB write if dkcksum32 gate were bypassed.
- Required config: N/A.
Recommended fix
--- a/sys/kern/subr_disklabel32.c
+++ b/sys/kern/subr_disklabel32.c
@@ -589,7 +589,8 @@
if (lp->d_secpercyl <= 0)
return ("fixlabel: d_secpercyl <= 0");
pp -= RAW_PART;
- warned = FALSE;
+ if (lp->d_npartitions > MAXPARTITIONS32)
+ lp->d_npartitions = MAXPARTITIONS32;
+ warned = FALSE;
for (part = 0; part < lp->d_npartitions; part++, pp++) {
Timeline
- 2026-06-30 Discovered during automated audit.