freebsd_network: Adjust address validation in bus_dma code.

We need not (in fact, must not) revalidate addresses if we are just
going to coalesce with the previous segment; otherwise, we will incorrectly
reject buffers that are more than page-aligned.

Should fix #17798 and the new KDL in #14260.
This commit is contained in:
Augustin Cavalier 2022-06-13 15:05:25 -04:00
parent 73603a5163
commit 7de24641a4

View File

@ -340,8 +340,6 @@ _bus_load_buffer(bus_dma_tag_t dmat, void* buf, bus_size_t buflen,
while (buflen > 0) { while (buflen > 0) {
const bus_addr_t phys_addr = pmap_kextract(virtual_addr); const bus_addr_t phys_addr = pmap_kextract(virtual_addr);
if (!_validate_address(dmat, phys_addr))
return ERANGE;
bus_size_t segment_size = PAGESIZE - (phys_addr & PAGE_MASK); bus_size_t segment_size = PAGESIZE - (phys_addr & PAGE_MASK);
if (segment_size > buflen) if (segment_size > buflen)
@ -356,25 +354,25 @@ _bus_load_buffer(bus_dma_tag_t dmat, void* buf, bus_size_t buflen,
segment_size = (boundary_addr - phys_addr); segment_size = (boundary_addr - phys_addr);
} }
// Insert chunk into a segment. // If possible, coalesce into the previous segment.
if (first) { if (!first && phys_addr == last_phys_addr
&& (segs[seg].ds_len + segment_size) <= dmat->maxsegsz
&& (dmat->boundary == 0
|| (segs[seg].ds_addr & boundary_mask)
== (phys_addr & boundary_mask))) {
// No need to validate the address here.
segs[seg].ds_len += segment_size;
} else {
if (first)
first = false;
else if (++seg >= dmat->maxsegments)
break;
if (!_validate_address(dmat, phys_addr))
return ERANGE;
segs[seg].ds_addr = phys_addr; segs[seg].ds_addr = phys_addr;
segs[seg].ds_len = segment_size; segs[seg].ds_len = segment_size;
first = false;
} else {
// If possible, coalesce into the previous segment.
if (phys_addr == last_phys_addr
&& (segs[seg].ds_len + segment_size) <= dmat->maxsegsz
&& (dmat->boundary == 0
|| (segs[seg].ds_addr & boundary_mask)
== (phys_addr & boundary_mask))) {
segs[seg].ds_len += segment_size;
} else {
if (++seg >= dmat->maxsegments)
break;
segs[seg].ds_addr = phys_addr;
segs[seg].ds_len = segment_size;
}
} }
last_phys_addr = phys_addr + segment_size; last_phys_addr = phys_addr + segment_size;