pmap_extract() KVA path duplicates the PDE validity check instead of testing the PTE β bogus non-zero physical address returned for unmapped kernel VA
Summary
Kernel branch re-tests '*pt & PG_V' (the PDE, already tested) instead of '*ptep & PG_V': with a present PT page but zero PTE it returns (va & PAGE_MASK) as a physical address instead of 0. No live in-tree KVA caller today (busdma uses pmap_kextract; sglist uses the correct user branch) - landmine for future callers like _sglist_append_buf DMA programming. One-operand fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5245 Β· 2 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.4 KB | β raw | |
| verdict.json | β | 2.0 KB | view raw |
DF-5245 β pmap_extract() KVA path checks the PDE instead of the PTE
What
pmap_extract() (sys/platform/pc64/x86_64/pmap.c:1717-1769), kernel-address
branch:
1733 pt = pmap_pt(pmap, va); /* PD slot (pointer to PT entry) */
1734 if (pt && (*pt & pmap->pmap_bits[PG_V_IDX])) { /* PDE valid */
1735 if (*pt & PG_PS) { ... 2MB case ok ... }
1737 else {
1738 ptep = pmap_pt_to_pte(*pt, va); /* PTE slot */
1739 if (*pt & pmap->pmap_bits[PG_V_IDX]) { /* BUG: re-tests *pt
(the PDE), should
test *ptep */
1740 rtval = *ptep & PG_FRAME;
1741 rtval |= va & PAGE_MASK;
1742 }
}
}
Line 1739 duplicates the PDE validity test instead of testing the PTE
(*ptep). When the page-table page exists (PDE valid) but the PTE is 0 (page
not entered / already removed), pmap_extract() returns
(va & PAGE_MASK) β the page offset β as a "valid" physical address instead
of 0 (its documented failure value).
Impact
Callers that use the return value without re-validating (e.g.
_sglist_append_buf() in sys/kern/subr_sglist.c:126-160, which feeds
ss_paddr straight into a scatter/gather list for DMA) can program a bogus
low physical address into device I/O. On pc64 the KVA branch of
pmap_extract() currently has no in-tree caller that passes a kernel VA
(busdma uses pmap_kextract(); sglist user paths use the correctly-checked
user branch at pmap.c:1756), so no live impact is demonstrated β filed as a
Low-severity correctness landmine with certain code-level confidence.
Fix
--- a/sys/platform/pc64/x86_64/pmap.c
+++ b/sys/platform/pc64/x86_64/pmap.c
@@ -1736,7 +1736,7 @@
} else {
ptep = pmap_pt_to_pte(*pt, va);
- if (*pt & pmap->pmap_bits[PG_V_IDX]) {
+ if (*ptep & pmap->pmap_bits[PG_V_IDX]) {
rtval = *ptep & PG_FRAME;
rtval |= va & PAGE_MASK;
}
Verification
Not executed on the guest: the buggy branch is not reachable from any
in-tree caller with a kernel VA on this platform (verified by grepping all
pmap_extract call sites), so there is no runtime trigger to build β
finding is certain by inspection, severity Low, no runtime impact
demonstrated. No fix.diff beyond the one-liner above (also embedded here and
in the finding markdown).
Fix verification
not_testablenot tested - trivial one-line correctness fix; branch has no reachable caller on pc64
README.md contains the one-line diff
Confirmed kernel references
Detail
Evidence (decisive lines)
README.md in this pack: quoted code pmap.c:1733-1743 showing the duplicated *pt test; caller survey (sys/kern/subr_sglist.c:126, sys/vfs/procfs/procfs_map.c:128) showing the KVA branch is dead on pc64 today
PoC changes
none - no runtime PoC (Low severity, dead branch on this platform; contract allows skipping verification for Low/Info)
Verified recommended fix
pmap.c:1739: change 'if (pt & pmap->pmap_bits[PG_V_IDX])' to 'if (ptep & pmap->pmap_bits[PG_V_IDX])'.
Verdict
pmap_extract() kernel-VA branch re-tests the PDE (pt) where it must test the PTE (ptep) at pmap.c:1739, so a not-present PTE under a valid page directory yields rtval = (va & PAGE_MASK) instead of the documented failure value 0. Certain by inspection. No runtime trigger exists on pc64: no in-tree caller reaches this branch with a kernel VA (busdma uses pmap_kextract; sglist/procfs use the user branch), so impact is latent (any future caller trusting the return, e.g. for DMA sglists, would receive a plausible-looking bogus physical address). Low severity, not executed on the guest.
No comments yet.