summaryrefslogtreecommitdiff
path: root/block/bio.c
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-09-07 17:37:28 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-09-07 17:37:28 +0200
commit5eccd39d8efa3bc8d557be50f202bbf023837eed (patch)
tree6f1589cd863481a7dfdace2b9a692957dc94afbe /block/bio.c
parent8fb649f3a174efaa618928cbe0737bf78b456eed (diff)
parent5015d0d945b3d3f2b038d2667880d5762f7d9437 (diff)
downloadlinux-rolling-stable.tar.gz
linux-rolling-stable.zip
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'block/bio.c')
-rw-r--r--block/bio.c56
1 files changed, 53 insertions, 3 deletions
diff --git a/block/bio.c b/block/bio.c
index 6a2f6fc3413e..5018a6fc2f36 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1221,10 +1221,45 @@ static int bio_iov_iter_align_down(struct bio *bio, struct iov_iter *iter,
return 0;
}
+#ifdef CONFIG_DEBUG_KERNEL
+static inline bool bio_iov_bvec_aligned(const struct bio *bio,
+ unsigned mem_align_mask)
+{
+ struct bvec_iter iter;
+ struct bio_vec bv;
+
+ /*
+ * Correct callers never break the alignment requirements, so this
+ * exhaustive check is only paid for in debug builds.
+ */
+ for_each_mp_bvec(bv, bio->bi_io_vec, iter, bio->bi_iter)
+ if ((bv.bv_offset | bv.bv_len) & mem_align_mask)
+ return false;
+ return true;
+}
+#else
+static inline bool bio_iov_bvec_aligned(const struct bio *bio,
+ unsigned mem_align_mask)
+{
+ /*
+ * We forward the bio_vec as-is, so ITER_BVEC callers must provide
+ * segments already aligned to the device's DMA alignment. The only
+ * unchecked user-controllable offset that reaches here is an io_uring
+ * registered buffer where just the first segment can be unaligned
+ * (the rest is virtually contiguous), so checking only that one is
+ * sufficient to know if the entire vector is valid.
+ */
+ return !(mp_bvec_iter_offset(bio->bi_io_vec, bio->bi_iter) &
+ mem_align_mask);
+}
+#endif
+
/**
* bio_iov_iter_get_pages - add user or kernel pages to a bio
* @bio: bio to add pages to
* @iter: iov iterator describing the region to be added
+ * @mem_align_mask: the mask the source address and length must be aligned to,
+ * 0 for no requirement
* @len_align_mask: the mask to align the total size to, 0 for any length
*
* This takes either an iterator pointing to user memory, or one pointing to
@@ -1243,7 +1278,7 @@ static int bio_iov_iter_align_down(struct bio *bio, struct iov_iter *iter,
* is returned only if 0 pages could be pinned.
*/
int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,
- unsigned len_align_mask)
+ unsigned mem_align_mask, unsigned len_align_mask)
{
iov_iter_extraction_t flags = 0;
@@ -1252,6 +1287,10 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,
if (iov_iter_is_bvec(iter)) {
bio_iov_bvec_set(bio, iter);
+
+ if (!bio_iov_bvec_aligned(bio, mem_align_mask))
+ return -EINVAL;
+
iov_iter_advance(iter, bio->bi_iter.bi_size);
return 0;
}
@@ -1266,8 +1305,19 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,
ret = iov_iter_extract_bvecs(iter, bio->bi_io_vec,
BIO_MAX_SIZE - bio->bi_iter.bi_size,
- &bio->bi_vcnt, bio->bi_max_vecs, flags);
+ &bio->bi_vcnt, bio->bi_max_vecs,
+ mem_align_mask, flags);
if (ret <= 0) {
+ /*
+ * A misaligned vector fails the whole I/O. Release any
+ * pages pinned by earlier iterations before returning
+ * since this bio won't be submitted to release them.
+ */
+ if (ret == -EINVAL) {
+ bio_release_pages(bio, false);
+ bio_clear_flag(bio, BIO_PAGE_PINNED);
+ bio->bi_vcnt = 0;
+ }
if (!bio->bi_vcnt)
return ret;
break;
@@ -1380,7 +1430,7 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter,
do {
ret = iov_iter_extract_bvecs(iter, bio->bi_io_vec + 1, len,
- &bio->bi_vcnt, bio->bi_max_vecs - 1, 0);
+ &bio->bi_vcnt, bio->bi_max_vecs - 1, 0, 0);
if (ret <= 0) {
if (!bio->bi_vcnt)
goto out_folio_put;