Skip to contentSkip to conversation

iov_iter: validate extracted segment for bvec alignment

PATCH In review Andrew Morton 1 participant 2 messages Oct 20

Subsystems (4)
lore
Share & tools
lore

Andrew Morton

author maintainer
#0 lore raw

PATCH 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.

Fixes 14b007e17881 ("block: validate user space vectors during extraction")
Assisted-by LLM
Cc Al Viro <[email protected]>
Cc Jens Axboe <[email protected]>
Cc Keith Busch <[email protected]>
Cc Hannes Reinecke <[email protected]>
Cc Christoph Hellwig <[email protected]>
Cc Haocheng Yu <[email protected]>
Cc Tal Zussman <[email protected]>

---

 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(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;

_

author maintainer Tested-by on v1 1/1
#1 lore raw
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() 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.

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. Keep first_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.