mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/3] Fix length calculation bug in extract_kvec_to_sg
@ 2026-02-20  9:38 Christian A. Ehrhardt
  2026-02-20  9:38 ` [PATCH 1/3] lib: kunit_iov_iter: Improve error detection Christian A. Ehrhardt
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Christian A. Ehrhardt @ 2026-02-20  9:38 UTC (permalink / raw)
  To: linux-kernel, Andrew Morton
  Cc: Christian A. Ehrhardt, Kees Cook, Petr Mladek, David Gow

There is a bug in extract_kvec_to_sg() where the length
of a scatterlist segment is miscalculated. The actual fix
is a one-liner and it is quite obvious from reading the
code that this is what was intened.

As this is a core library function this series first adds
test cases to the kunit_iov_iter test that demonstrate that
there is a bug before actually fixing it in the last commit.

The bug was orignally introduced into kernel v6.3 where the
function lived in fs/netfs/iterator.c. It was later moved
to lib/scatterlist.c in v6.5. Thus the actual fix is only
marked for backports to v6.5+.

Christian A. Ehrhardt (3):
  lib: kunit_iov_iter: Improve error detection
  lib: kunit_iov_iter: Add tests for extract_iter_to_sg
  lib: Fix length calculation in extract_kvec_to_sg

 lib/scatterlist.c          |   2 +-
 lib/tests/kunit_iov_iter.c | 147 ++++++++++++++++++++++++++++++++++++-
 2 files changed, 147 insertions(+), 2 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/3] lib: kunit_iov_iter: Improve error detection
  2026-02-20  9:38 [PATCH 0/3] Fix length calculation bug in extract_kvec_to_sg Christian A. Ehrhardt
@ 2026-02-20  9:38 ` Christian A. Ehrhardt
  2026-02-20  9:38 ` [PATCH 2/3] lib: kunit_iov_iter: Add tests for extract_iter_to_sg Christian A. Ehrhardt
  2026-02-20  9:38 ` [PATCH 3/3] lib: Fix length calculation in extract_kvec_to_sg Christian A. Ehrhardt
  2 siblings, 0 replies; 4+ messages in thread
From: Christian A. Ehrhardt @ 2026-02-20  9:38 UTC (permalink / raw)
  To: linux-kernel, Andrew Morton
  Cc: Christian A. Ehrhardt, David Howells, Kees Cook, Petr Mladek, David Gow

In the kunit_iov_iter test prevent the kernel buffer from
being a single physically contiguous region.

Additionally, make sure that the test pattern written to
a page in the buffer depends on the offset of the page within
the buffer.

Cc: David Howells <dhowells@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Christian A. Ehrhardt <lk@c--e.de>
---
 lib/tests/kunit_iov_iter.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/lib/tests/kunit_iov_iter.c b/lib/tests/kunit_iov_iter.c
index 48342736d016..c32fbdbb2544 100644
--- a/lib/tests/kunit_iov_iter.c
+++ b/lib/tests/kunit_iov_iter.c
@@ -13,6 +13,7 @@
 #include <linux/uio.h>
 #include <linux/bvec.h>
 #include <linux/folio_queue.h>
+#include <linux/minmax.h>
 #include <kunit/test.h>
 
 MODULE_DESCRIPTION("iov_iter testing");
@@ -37,7 +38,7 @@ static const struct kvec_test_range kvec_test_ranges[] = {
 
 static inline u8 pattern(unsigned long x)
 {
-	return x & 0xff;
+	return (u8)x + (u8)(x >> 8) + (u8)(x >> 16);
 }
 
 static void iov_kunit_unmap(void *data)
@@ -52,6 +53,7 @@ static void *__init iov_kunit_create_buffer(struct kunit *test,
 	struct page **pages;
 	unsigned long got;
 	void *buffer;
+	unsigned int i;
 
 	pages = kunit_kcalloc(test, npages, sizeof(struct page *), GFP_KERNEL);
         KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pages);
@@ -62,6 +64,9 @@ static void *__init iov_kunit_create_buffer(struct kunit *test,
 		release_pages(pages, got);
 		KUNIT_ASSERT_EQ(test, got, npages);
 	}
+	/* Make sure that we don't get a physically contiguous buffer. */
+	for (i = 0; i < npages / 4; ++i)
+		swap(pages[i], pages[i + npages/2]);
 
 	buffer = vmap(pages, npages, VM_MAP | VM_MAP_PUT_PAGES, PAGE_KERNEL);
         KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buffer);
-- 
2.43.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 2/3] lib: kunit_iov_iter: Add tests for extract_iter_to_sg
  2026-02-20  9:38 [PATCH 0/3] Fix length calculation bug in extract_kvec_to_sg Christian A. Ehrhardt
  2026-02-20  9:38 ` [PATCH 1/3] lib: kunit_iov_iter: Improve error detection Christian A. Ehrhardt
@ 2026-02-20  9:38 ` Christian A. Ehrhardt
  2026-02-20  9:38 ` [PATCH 3/3] lib: Fix length calculation in extract_kvec_to_sg Christian A. Ehrhardt
  2 siblings, 0 replies; 4+ messages in thread
From: Christian A. Ehrhardt @ 2026-02-20  9:38 UTC (permalink / raw)
  To: linux-kernel, Andrew Morton
  Cc: Christian A. Ehrhardt, David Howells, Kees Cook, Petr Mladek, David Gow

Add test cases that test extract_iter_to_sg.

For each iterator type an iterator is loaded with a kernel
buffer. The iterator is then extracted to a scatterlist and
the scatterlist is copied into a scratch buffer.

The test passes if the scratch buffer compares equal to the
original buffer.

The new tests demostrate a bug in extract_iter_to_sg
for kvec iterators and one of the tests will fail.  This
is fixed in the next commit.

Cc: David Howells <dhowells@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Christian A. Ehrhardt <lk@c--e.de>
---
 lib/tests/kunit_iov_iter.c | 140 +++++++++++++++++++++++++++++++++++++
 1 file changed, 140 insertions(+)

diff --git a/lib/tests/kunit_iov_iter.c b/lib/tests/kunit_iov_iter.c
index c32fbdbb2544..aa6a19ca6dad 100644
--- a/lib/tests/kunit_iov_iter.c
+++ b/lib/tests/kunit_iov_iter.c
@@ -13,6 +13,7 @@
 #include <linux/uio.h>
 #include <linux/bvec.h>
 #include <linux/folio_queue.h>
+#include <linux/scatterlist.h>
 #include <linux/minmax.h>
 #include <kunit/test.h>
 
@@ -1014,6 +1015,141 @@ static void __init iov_kunit_extract_pages_xarray(struct kunit *test)
 	KUNIT_SUCCEED(test);
 }
 
+struct iov_kunit_iter_to_sg_data {
+	struct sg_table sgt;
+	u8 *buffer, *scratch;
+	struct page **pages;
+	size_t npages;
+};
+
+static void __init
+iov_kunit_iter_to_sg_init(struct kunit *test, size_t bufsize,
+			  struct iov_kunit_iter_to_sg_data *data)
+{
+	struct page **spages;
+	struct scatterlist *sg;
+	size_t i;
+
+	data->npages = bufsize / PAGE_SIZE;
+	sg = kunit_kmalloc_array(test, data->npages, sizeof(*sg), GFP_KERNEL);
+	sg_init_table(sg, data->npages);
+	memset(&data->sgt, 0, sizeof(data->sgt));
+	data->sgt.orig_nents = data->npages;
+	data->sgt.sgl = sg;
+
+	data->buffer = iov_kunit_create_buffer(test, &data->pages,
+					       data->npages);
+	data->scratch = iov_kunit_create_buffer(test, &spages, data->npages);
+	for (i = 0; i < bufsize; ++i)
+		data->buffer[i] = pattern(i);
+	memset(data->scratch, 0, bufsize);
+}
+
+static void __init
+iov_kunit_iter_to_sg_check(struct kunit *test, struct iov_iter *iter,
+			   size_t bufsize,
+			   struct iov_kunit_iter_to_sg_data *data)
+{
+	size_t i;
+
+	i = extract_iter_to_sg(iter, bufsize, &data->sgt,
+			       data->npages, 0);
+
+	KUNIT_EXPECT_EQ(test, i, bufsize);
+	KUNIT_EXPECT_LE(test, data->sgt.nents, data->npages);
+
+	i = sg_copy_to_buffer(data->sgt.sgl, data->sgt.nents,
+			      data->scratch, bufsize);
+	KUNIT_EXPECT_EQ(test, i, bufsize);
+
+	for (i = 0; i < bufsize; ++i) {
+		KUNIT_EXPECT_EQ_MSG(test, data->buffer[i], data->scratch[i],
+				    "at i=%zx", i);
+		if (data->buffer[i] != data->scratch[i])
+			break;
+	}
+
+	KUNIT_EXPECT_EQ(test, i, bufsize);
+}
+
+static void __init iov_kunit_iter_to_sg_kvec(struct kunit *test)
+{
+	struct iov_kunit_iter_to_sg_data data;
+	struct iov_iter iter;
+	struct kvec kvec;
+	size_t bufsize;
+
+	bufsize = 0x100000;
+	iov_kunit_iter_to_sg_init(test, bufsize, &data);
+
+	kvec.iov_base = data.buffer;
+	kvec.iov_len = bufsize;
+	iov_iter_kvec(&iter, READ, &kvec, 1, bufsize);
+
+	iov_kunit_iter_to_sg_check(test, &iter, bufsize, &data);
+}
+
+static void __init iov_kunit_iter_to_sg_bvec(struct kunit *test)
+{
+	struct iov_kunit_iter_to_sg_data data;
+	struct page *p, *can_merge = NULL;
+	size_t i, k, bufsize;
+	struct bio_vec *bvec;
+	struct iov_iter iter;
+
+	bufsize = 0x100000;
+	iov_kunit_iter_to_sg_init(test, bufsize, &data);
+
+	bvec = kunit_kmalloc_array(test, data.npages, sizeof(*bvec),
+				   GFP_KERNEL);
+	k = 0;
+	for (i = 0; i < data.npages; ++i) {
+		p = data.pages[i];
+		if (p == can_merge)
+			bvec[k-1].bv_len += PAGE_SIZE;
+		else
+			bvec_set_page(&bvec[k++], p, PAGE_SIZE, 0);
+		can_merge = p + 1;
+	}
+	iov_iter_bvec(&iter, READ, bvec, k, bufsize);
+
+	iov_kunit_iter_to_sg_check(test, &iter, bufsize, &data);
+}
+
+static void __init iov_kunit_iter_to_sg_folioq(struct kunit *test)
+{
+	struct iov_kunit_iter_to_sg_data data;
+	struct folio_queue *folioq;
+	struct iov_iter iter;
+	size_t bufsize;
+
+	bufsize = 0x100000;
+	iov_kunit_iter_to_sg_init(test, bufsize, &data);
+
+	folioq = iov_kunit_create_folioq(test);
+	iov_kunit_load_folioq(test, &iter, READ, folioq, data.pages,
+			      data.npages);
+
+	iov_kunit_iter_to_sg_check(test, &iter, bufsize, &data);
+}
+
+static void __init iov_kunit_iter_to_sg_xarray(struct kunit *test)
+{
+	struct iov_kunit_iter_to_sg_data data;
+	struct xarray *xarray;
+	struct iov_iter iter;
+	size_t bufsize;
+
+	bufsize = 0x100000;
+	iov_kunit_iter_to_sg_init(test, bufsize, &data);
+
+	xarray = iov_kunit_create_xarray(test);
+	iov_kunit_load_xarray(test, &iter, READ, xarray, data.pages,
+			      data.npages);
+
+	iov_kunit_iter_to_sg_check(test, &iter, bufsize, &data);
+}
+
 static struct kunit_case __refdata iov_kunit_cases[] = {
 	KUNIT_CASE(iov_kunit_copy_to_kvec),
 	KUNIT_CASE(iov_kunit_copy_from_kvec),
@@ -1027,6 +1163,10 @@ static struct kunit_case __refdata iov_kunit_cases[] = {
 	KUNIT_CASE(iov_kunit_extract_pages_bvec),
 	KUNIT_CASE(iov_kunit_extract_pages_folioq),
 	KUNIT_CASE(iov_kunit_extract_pages_xarray),
+	KUNIT_CASE(iov_kunit_iter_to_sg_kvec),
+	KUNIT_CASE(iov_kunit_iter_to_sg_bvec),
+	KUNIT_CASE(iov_kunit_iter_to_sg_folioq),
+	KUNIT_CASE(iov_kunit_iter_to_sg_xarray),
 	{}
 };
 
-- 
2.43.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 3/3] lib: Fix length calculation in extract_kvec_to_sg
  2026-02-20  9:38 [PATCH 0/3] Fix length calculation bug in extract_kvec_to_sg Christian A. Ehrhardt
  2026-02-20  9:38 ` [PATCH 1/3] lib: kunit_iov_iter: Improve error detection Christian A. Ehrhardt
  2026-02-20  9:38 ` [PATCH 2/3] lib: kunit_iov_iter: Add tests for extract_iter_to_sg Christian A. Ehrhardt
@ 2026-02-20  9:38 ` Christian A. Ehrhardt
  2 siblings, 0 replies; 4+ messages in thread
From: Christian A. Ehrhardt @ 2026-02-20  9:38 UTC (permalink / raw)
  To: linux-kernel, Andrew Morton
  Cc: Christian A. Ehrhardt, David Howells, Kees Cook, Petr Mladek, David Gow

When extracting from a kvec to a scatterlist, do not
cross page boundaries. The required length is already
calculated but not used as intended.

The previous changes to the kunit_iov_iter.c demonstrate
that the patch is necessary.

Cc: David Howells <dhowells@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: stable@vger.kernel.org # v6.5+
Fixes: 018584697533 ("netfs: Add a function to extract an iterator into a scatterlist")
Signed-off-by: Christian A. Ehrhardt <lk@c--e.de>
---
 lib/scatterlist.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/scatterlist.c b/lib/scatterlist.c
index 21bc9c1f7c06..73893ee0d92d 100644
--- a/lib/scatterlist.c
+++ b/lib/scatterlist.c
@@ -1249,7 +1249,7 @@ static ssize_t extract_kvec_to_sg(struct iov_iter *iter,
 			else
 				page = virt_to_page((void *)kaddr);
 
-			sg_set_page(sg, page, len, off);
+			sg_set_page(sg, page, seg, off);
 			sgtable->nents++;
 			sg++;
 			sg_max--;
-- 
2.43.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-02-20  9:39 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-02-20  9:38 [PATCH 0/3] Fix length calculation bug in extract_kvec_to_sg Christian A. Ehrhardt
2026-02-20  9:38 ` [PATCH 1/3] lib: kunit_iov_iter: Improve error detection Christian A. Ehrhardt
2026-02-20  9:38 ` [PATCH 2/3] lib: kunit_iov_iter: Add tests for extract_iter_to_sg Christian A. Ehrhardt
2026-02-20  9:38 ` [PATCH 3/3] lib: Fix length calculation in extract_kvec_to_sg Christian A. Ehrhardt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®