iov_iter: validate extracted segment for bvec alignment
Andrew Morton
maintainerPATCH 1/1 iov_iter: validate extracted segment for bvec alignment
From: Daehyeon Ko <[email protected]>
Subject: iov_iter: validate extracted segment for bvec alignment
Date: Thu, 15 Oct 2026 15:19:07 +0900
iov_iter_extract_bvecs() validates iter_iov_addr() and iter_iov_len() before iov_iter_extract_pages() selects the first nonempty segment and limits it by iter->count. The checks can therefore cover a different range after a leading empty segment or iov_iter_truncate().
An unprivileged O_DIRECT preadv() reproduced the empty-segment case: an aligned empty segment followed by a misaligned one completed 4096 bytes instead of returning -EINVAL. A truncated iterator has the same mismatch when its full segment length is aligned but its remaining count is not.
The worst effect I have confirmed is an alignment-contract bypass. An unprivileged O_DIRECT preadv() produced bvecs with offset/length pairs (1, 4095) and (0, 1). The bio reached brd_submit_bio(), and the read completed 4096 bytes instead of failing with -EINVAL.
I have not demonstrated harmful behavior on real DMA hardware. For a normal blk-mq target, blk_mq_submit_bio() calls __bio_split_to_limits(), where bio_split_io_at() rejects the misaligned bvec before queue_rq(). Virtio-blk and loop both returned -EINVAL in my controls.
So a bio-based driver can receive a bvec which violates its advertised dma_alignment, but any further effect is driver-specific. I have not reproduced memory corruption or a crash on real hardware.
Bound the normal fast-path length by iter->count. Keep first_iovec_segment() for leading empty segments so validation covers the remaining portion of the segment available for extraction.
---
lib/iov_iter.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
lib/iov_iter.c renamed from lib/iov_iter.c~iov_iter-validate-extracted-segment-for-bvec-alignment +7 -2
@@ -1022,7 +1022,7 @@ static ssize_t iter_xarray_get_pages(strreturn maxsize; } -/* must be done on non-empty ITER_UBUF or ITER_IOVEC one */ +/* must be used on a non-empty ITER_UBUF, ITER_IOVEC or ITER_KVEC */ static unsigned long first_iovec_segment (const struct iov_iter *i,size_t *size) {size_t skip;
@@ -1938,8 +1938,13 @@ ssize_t iov_iter_extract_bvecs(struct io iter_is_iovec(iter) || iov_iter_is_kvec(iter))) {unsigned long start = (unsigned long )iter_iov_addr (iter); +size_t seg_size =min (iter_iov_len (iter), iter->count); -if ((start |iter_iov_len (iter)) & mem_align_mask) +if (unlikely (!seg_size && iter->count)) { + seg_size = iter->count; + start =first_iovec_segment (iter, &seg_size); + } +if ((start | seg_size) & mem_align_mask)return -EINVAL ; } else if (iov_iter_alignment(iter) & mem_align_mask) {return -EINVAL ;
_
Quoted 25 lines · On Tue, 20 Oct 2026 11:43:32 -0700 Andrew Morton <[email protected]>…
From: Daehyeon Ko <[email protected]>
Subject:iov_iter: validate extracted segment for bvec alignment
Date: Thu, 15 Oct 2026 15:19:07 +0900
iov_iter_extract_bvecs()validatesiter_iov_addr()anditer_iov_len()beforeiov_iter_extract_pages()selects the first nonempty segment and limits it byiter->count. The checks can therefore cover a different range after a leading empty segment oriov_iter_truncate().An unprivileged
O_DIRECTpreadv()reproduced the empty-segment case: an aligned empty segment followed by a misaligned one completed 4096 bytes instead of returning-EINVAL. A truncated iterator has the same mismatch when its full segment length is aligned but its remaining count is not.The worst effect I have confirmed is an alignment-contract bypass. An unprivileged
O_DIRECTpreadv()produced bvecs with offset/length pairs (1, 4095) and (0, 1). The bio reachedbrd_submit_bio(), and the read completed 4096 bytes instead of failing with-EINVAL.I have not demonstrated harmful behavior on real DMA hardware. For a normal blk-mq target,
blk_mq_submit_bio()calls__bio_split_to_limits(), wherebio_split_io_at()rejects the misaligned bvec beforequeue_rq(). Virtio-blk and loop both returned-EINVALin my controls.
Quoted 7 lines
So a bio-based driver can receive a bvec which violates its advertised
dma_alignment, but any further effect is driver-specific. I have not reproduced memory corruption or a crash on real hardware.Bound the normal fast-path length by
iter->count. Keepfirst_iovec_segment()for leading empty segments so validation covers the remaining portion of the segment available for extraction.
I guess I should have added
I tested this against the ext4 writev() KASAN reproducer. The unpatched kernel triggered a slab out-of-bounds read and returned 1015 bytes. With the patch applied, writev() returned the expected 1025 bytes without a KASAN report. But I didn't test the preadv() path.