From: Daehyeon Ko <4ncienth@gmail.com> 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. Link: https://lore.kernel.org/20261015061907.2713943-1-4ncienth@gmail.com Fixes: 14b007e17881 ("block: validate user space vectors during extraction") Signed-off-by: Daehyeon Ko <4ncienth@gmail.com> Signed-off-by: Andrew Morton Closes: https://lore.kernel.org/r/20261012065520.1089147-1-4ncienth@gmail.com Assisted-by: LLM Cc: Al Viro Cc: Jens Axboe Cc: Keith Busch Cc: Hannes Reinecke Cc: Christoph Hellwig Cc: Haocheng Yu Cc: Tal Zussman Cc: --- lib/iov_iter.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) --- a/lib/iov_iter.c~iov_iter-validate-extracted-segment-for-bvec-alignment +++ a/lib/iov_iter.c @@ -1022,7 +1022,7 @@ static ssize_t iter_xarray_get_pages(str return 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; _