iov_iter_extract_bvecs() checks the current IOVEC or KVEC segment against the block device's DMA alignment before extracting pages. Empty segments are valid, but the check uses iter_iov_addr() and iter_iov_len() directly while first_iovec_segment(), used by the extraction path, skips them. An aligned empty segment followed by a misaligned nonempty segment therefore passes the check and produces misaligned bvecs. An unprivileged O_DIRECT preadv() reproduced this on a regular ext4 file above brd. The equivalent nonempty-leading vector returned -EINVAL, while the empty-leading vector completed 4096 bytes. This bypasses the validation added for CVE-2026-89584. When the current segment is empty but the iterator still contains data, use first_iovec_segment() to select and validate the same segment that page extraction will use. Keep the existing constant-time fast path for the normal nonempty case. Fixes: 14b007e17881 ("block: validate user space vectors during extraction") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Daehyeon Ko <4ncienth@gmail.com> --- lib/iov_iter.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/iov_iter.c b/lib/iov_iter.c index 2072c04e99d052..893de96de8ab6a 100644 --- a/lib/iov_iter.c +++ b/lib/iov_iter.c @@ -1938,8 +1938,13 @@ ssize_t iov_iter_extract_bvecs(struct iov_iter *iter, struct bio_vec *bv, iter_is_iovec(iter) || iov_iter_is_kvec(iter))) { unsigned long start = (unsigned long)iter_iov_addr(iter); + size_t seg_size = iter_iov_len(iter); - 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; base-commit: d6d3455d8751854e0d1820f14184682e23ac4475 -- 2.55.0