cluster_rbuild() under-counts component pages vs the pbuf's embedded xio_pages[]/KVA window β latent OOB pointer write + PTE install past MAXPHYS window for non-page-tiling VFS geometry
| Field | Value |
|---|---|
| ID | DF-2725 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:H/A:H |
| CWE | CWE-787 / CWE-131 |
| File | sys/kern/vfs_cluster.c |
| Lines | 954-959 (check), 1049-1066 (append), 1094-1100 |
| Area | kern |
| Confidence | likely |
| Discovered | 2026-08-30 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | memcorrupt |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
cluster_rbuild() admits a new component whenever
(npages*PAGE_SIZE)+round_page(blksize) <= maxiosize and performs no
capacity check at all for the first component β but each component
actually contributes tbp->b_xio.xio_npages pages, which can exceed
round_page(blksize)/PAGE_SIZE by 1 whenever a component buffer
starts mid-page and crosses a page boundary. cluster_wbuild() gets
this right by summing actual page counts. With non-page-tiling
geometry the loop can accumulate more than maxiosize/PAGE_SIZE pages
into bp->b_xio.xio_pages β the embedded array is exactly
XIO_INTERNAL_PAGES=btoc(MAXPHYS) with kvasize=MAXPHYS β producing
(a) OOB writes of vm_page_t pointers past the embedded array in
struct buf and (b) pmap_qenter_noinval() installing PTEs past the
reserved MAXPHYS KVA window. The b_bufsize>b_kvasize panic does NOT
catch this (b_bufsize aggregates bytes, not pages). LATENT in-tree:
every in-tree caller produces page-tiling buffers (ffs/ext2 fragment
offsets are fsize-multiples and fsize|PAGE; msdosfs power-of-2
blsize at cluster-aligned offsets; tmpfs/fuse PAGE_SIZE; hammer 16K;
hammer2 uniform 64K; GETBLK_SZMATCH pins component size==blksize).
Reachable by any out-of-tree/third-party VFS (or future in-tree one)
whose block geometry is not page-tiling β then a plain sequential
read loop drives it.
Recommended fix
Mirror cluster_wbuild's exact accounting before appending each component's pages (test `bp->b_xio.xio_npages + tbp->b_xio.xio_npages
maxiosize / PAGE_SIZE` after the getblk, as :1609-1610 does) and cap the first component too.
Timeline
- 2026-08-30 Discovered during pass-2 audit of vfs_cluster.c (GLM 5.3).
No comments yet.