mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2 0/2] libceph: validate OSD sparse-read extent maps (+KUnit)
@ 2026-07-14 11:51 Michael Bommarito
  2026-07-14 11:51 ` [PATCH v2 1/2] libceph: validate OSD extent maps before cursor advance Michael Bommarito
  2026-07-14 11:51 ` [PATCH v2 2/2] libceph: add KUnit coverage for OSD sparse-read extent validation Michael Bommarito
  0 siblings, 2 replies; 4+ messages in thread
From: Michael Bommarito @ 2026-07-14 11:51 UTC (permalink / raw)
  To: Ilya Dryomov; +Cc: Viacheslav Dubeyko, ceph-devel, linux-kernel, stable

Patch 1 is the fix from v1 (unchanged), now carrying Viacheslav Dubeyko's
Reviewed-by. Patch 2 adds the KUnit coverage he asked for.

osd_sparse_read() checks that the sparse-read data length matches the summed
extent lengths but never checks that each OSD-supplied extent is monotonic
and stays inside the original request range, so a malformed authenticated
OSD reply can advance the message-data cursor past the request buffer and
hit the BUG_ON(!*length) in ceph_msg_data_next(). Patch 1 rejects such maps
before advancing the cursor.

The KUnit suite in patch 2 drives the real osd_sparse_read() state machine
over a synthesized reply (no OSD, no network). Confirmed before/after on
v7.2-rc3: without patch 1 the out-of-range case faults in
ceph_msg_data_next() while the in-range control passes (1 pass, 1 fail);
with patch 1 both pass.

v1: https://lore.kernel.org/ceph-devel/20260710022837.libceph-sparse-v1@bommarito/

Michael Bommarito (2):
  libceph: validate OSD extent maps before cursor advance
  libceph: add KUnit coverage for OSD sparse-read extent validation

 net/ceph/Kconfig            |  12 +++
 net/ceph/osd_client-kunit.c | 146 ++++++++++++++++++++++++++++++++++++
 net/ceph/osd_client.c       |  34 +++++++++
 3 files changed, 192 insertions(+)
 create mode 100644 net/ceph/osd_client-kunit.c

-- 
2.53.0


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

* [PATCH v2 1/2] libceph: validate OSD extent maps before cursor advance
  2026-07-14 11:51 [PATCH v2 0/2] libceph: validate OSD sparse-read extent maps (+KUnit) Michael Bommarito
@ 2026-07-14 11:51 ` Michael Bommarito
  2026-07-14 11:51 ` [PATCH v2 2/2] libceph: add KUnit coverage for OSD sparse-read extent validation Michael Bommarito
  1 sibling, 0 replies; 4+ messages in thread
From: Michael Bommarito @ 2026-07-14 11:51 UTC (permalink / raw)
  To: Ilya Dryomov; +Cc: Viacheslav Dubeyko, ceph-devel, linux-kernel, stable

net/ceph/osd_client.c:osd_sparse_read() validates that the sparse-read
data length matches the summed extent lengths, but it does not validate
that each OSD-supplied extent is monotonic and lies inside the original
request range. A malformed authenticated OSD reply can advertise a
far-forward nonzero extent offset with a matching data length and make
the client advance the message-data cursor beyond the request buffer.
This reaches the BUG_ON(!*length) assertion in ceph_msg_data_next() from
the client receive path.

Impact: A malicious or compromised authenticated Ceph OSD peer can crash
a kernel Ceph client via a malformed sparse-read reply.

Reject sparse extent maps that overflow, move backwards, overlap, or
extend outside the original sparse-read request before advancing the
cursor.

Fixes: f628d7999727 ("libceph: add sparse read support to OSD client")
Cc: stable@vger.kernel.org
Reviewed-by: Viacheslav Dubeyko <slava@dubeyko.com>
Assisted-by: Codex:gpt-5-5-xhigh
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
 net/ceph/osd_client.c | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
index 2ff00070c1810..76ba3abdad9b1 100644
--- a/net/ceph/osd_client.c
+++ b/net/ceph/osd_client.c
@@ -6,6 +6,7 @@
 #include <linux/err.h>
 #include <linux/highmem.h>
 #include <linux/mm.h>
+#include <linux/overflow.h>
 #include <linux/pagemap.h>
 #include <linux/slab.h>
 #include <linux/uaccess.h>
@@ -5799,6 +5800,31 @@ static inline void convert_extent_map(struct ceph_sparse_read *sr)
 }
 #endif
 
+static bool sparse_extent_map_valid(struct ceph_sparse_read *sr)
+{
+	u64 req_end, pos;
+	int i;
+
+	if (check_add_overflow(sr->sr_req_off, sr->sr_req_len, &req_end))
+		return false;
+
+	pos = sr->sr_req_off;
+	for (i = 0; i < sr->sr_count; i++) {
+		struct ceph_sparse_extent *ext = &sr->sr_extent[i];
+		u64 end;
+
+		if (ext->off < pos)
+			return false;
+		if (check_add_overflow(ext->off, ext->len, &end))
+			return false;
+		if (end > req_end)
+			return false;
+		pos = end;
+	}
+
+	return true;
+}
+
 static int osd_sparse_read(struct ceph_connection *con,
 			   struct ceph_msg_data_cursor *cursor,
 			   char **pbuf)
@@ -5856,6 +5882,10 @@ static int osd_sparse_read(struct ceph_connection *con,
 	case CEPH_SPARSE_READ_DATA_PRE:
 		/* Convert sr_datalen to host-endian */
 		sr->sr_datalen = le32_to_cpu((__force __le32)sr->sr_datalen);
+		if (!sparse_extent_map_valid(sr)) {
+			pr_warn_ratelimited("invalid sparse extent map\n");
+			return -EREMOTEIO;
+		}
 		for (i = 0; i < count; i++)
 			len += sr->sr_extent[i].len;
 		if (sr->sr_datalen != len) {
-- 
2.53.0


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

* [PATCH v2 2/2] libceph: add KUnit coverage for OSD sparse-read extent validation
  2026-07-14 11:51 [PATCH v2 0/2] libceph: validate OSD sparse-read extent maps (+KUnit) Michael Bommarito
  2026-07-14 11:51 ` [PATCH v2 1/2] libceph: validate OSD extent maps before cursor advance Michael Bommarito
@ 2026-07-14 11:51 ` Michael Bommarito
  2026-07-14 17:43   ` Viacheslav Dubeyko
  1 sibling, 1 reply; 4+ messages in thread
From: Michael Bommarito @ 2026-07-14 11:51 UTC (permalink / raw)
  To: Ilya Dryomov; +Cc: Viacheslav Dubeyko, ceph-devel, linux-kernel, stable

Add KUnit coverage for the sparse-read extent-map validation added by the
previous patch. The tests drive the real osd_sparse_read() state machine
over a synthesized OSD reply (no OSD or network): an in-range single
extent is accepted and advances the cursor, and an extent whose offset
lies outside the original request range is rejected with -EREMOTEIO before
the message-data cursor is advanced.

The suite is included from osd_client.c so it can exercise the static
sparse-read parser and cursor-advance helpers without exporting test-only
symbols, guarded by CONFIG_CEPH_LIB_KUNIT_TEST.

Assisted-by: Codex:gpt-5-5-xhigh
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
 net/ceph/Kconfig            |  12 +++
 net/ceph/osd_client-kunit.c | 146 ++++++++++++++++++++++++++++++++++++
 net/ceph/osd_client.c       |   4 +
 3 files changed, 162 insertions(+)
 create mode 100644 net/ceph/osd_client-kunit.c

diff --git a/net/ceph/Kconfig b/net/ceph/Kconfig
index 7e2528cde4b94..6b78fe4a0f8a4 100644
--- a/net/ceph/Kconfig
+++ b/net/ceph/Kconfig
@@ -45,3 +45,15 @@ config CEPH_LIB_USE_DNS_RESOLVER
 	  Documentation/networking/dns_resolver.rst
 
 	  If unsure, say N.
+
+config CEPH_LIB_KUNIT_TEST
+	bool "KUnit tests for the Ceph core library" if !KUNIT_ALL_TESTS
+	depends on CEPH_LIB && KUNIT
+	default KUNIT_ALL_TESTS
+	help
+	  This builds KUnit coverage for selected Ceph core-library parser
+	  and state-machine helpers. The tests exercise internal libceph
+	  behavior with synthesized state and are intended for developer
+	  validation rather than production systems.
+
+	  If unsure, say N.
diff --git a/net/ceph/osd_client-kunit.c b/net/ceph/osd_client-kunit.c
new file mode 100644
index 0000000000000..713f24675f68f
--- /dev/null
+++ b/net/ceph/osd_client-kunit.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit coverage for net/ceph/osd_client.c internals.
+ *
+ * Included from osd_client.c so the test can drive the real static sparse-read
+ * parser and cursor-advance helper without exporting test-only symbols.
+ */
+
+#include <kunit/test.h>
+
+struct ceph_osd_sparse_read_test {
+	struct ceph_osd osd;
+	struct ceph_connection con;
+	struct ceph_msg *msg;
+	struct ceph_msg_data_cursor cursor;
+	struct ceph_osd_request *req;
+	struct page **pages;
+	struct page *page;
+};
+
+static int ceph_osd_sparse_read_test_init(struct kunit *test)
+{
+	struct ceph_osd_sparse_read_test *ctx;
+
+	ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, ctx);
+
+	ctx->req = kunit_kzalloc(test, struct_size(ctx->req, r_ops, 1),
+				 GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, ctx->req);
+
+	ctx->pages = kunit_kcalloc(test, 1, sizeof(*ctx->pages), GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, ctx->pages);
+
+	ctx->page = alloc_page(GFP_KERNEL);
+	KUNIT_ASSERT_NOT_NULL(test, ctx->page);
+	ctx->pages[0] = ctx->page;
+
+	ctx->msg = ceph_msg_new2(CEPH_MSG_OSD_OPREPLY, 0, 1, GFP_KERNEL, true);
+	KUNIT_ASSERT_NOT_NULL(test, ctx->msg);
+
+	osd_init(&ctx->osd);
+	ctx->osd.o_sparse_op_idx = -1;
+	ceph_init_sparse_read(&ctx->osd.o_sparse_read);
+
+	request_init(ctx->req);
+	ctx->req->r_tid = 1;
+	ctx->req->r_num_ops = 1;
+	osd_req_op_extent_init(ctx->req, 0, CEPH_OSD_OP_SPARSE_READ,
+			       0, PAGE_SIZE, 0, 0);
+	KUNIT_ASSERT_EQ(test, ctx->req->r_ops[0].op, CEPH_OSD_OP_SPARSE_READ);
+
+	insert_request(&ctx->osd.o_requests, ctx->req);
+
+	ctx->msg->hdr.tid = cpu_to_le64(ctx->req->r_tid);
+	ceph_msg_data_add_pages(ctx->msg, ctx->pages, PAGE_SIZE, 0, false);
+	ceph_msg_data_cursor_init(&ctx->cursor, ctx->msg, PAGE_SIZE);
+
+	ctx->con.private = &ctx->osd;
+	ctx->con.in_msg = ctx->msg;
+
+	test->priv = ctx;
+	return 0;
+}
+
+static void ceph_osd_sparse_read_test_exit(struct kunit *test)
+{
+	struct ceph_osd_sparse_read_test *ctx = test->priv;
+
+	if (!ctx)
+		return;
+
+	if (ctx->req && !RB_EMPTY_NODE(&ctx->req->r_node))
+		erase_request(&ctx->osd.o_requests, ctx->req);
+	ceph_init_sparse_read(&ctx->osd.o_sparse_read);
+	if (ctx->msg)
+		ceph_msg_put(ctx->msg);
+	if (ctx->page)
+		__free_page(ctx->page);
+}
+
+static int ceph_osd_sparse_read_feed_one_extent(struct kunit *test,
+						u64 off, u64 len, u32 datalen)
+{
+	struct ceph_osd_sparse_read_test *ctx = test->priv;
+	struct ceph_sparse_read *sr = &ctx->osd.o_sparse_read;
+	char *buf = NULL;
+	int ret;
+
+	ret = osd_sparse_read(&ctx->con, &ctx->cursor, &buf);
+	KUNIT_ASSERT_EQ(test, ret, (int)sizeof(sr->sr_count));
+	KUNIT_ASSERT_PTR_EQ(test, buf, (char *)&sr->sr_count);
+
+	sr->sr_count = (__force u32)cpu_to_le32(1);
+	ret = osd_sparse_read(&ctx->con, &ctx->cursor, &buf);
+	KUNIT_ASSERT_EQ(test, ret, (int)sizeof(*sr->sr_extent));
+	KUNIT_ASSERT_NOT_NULL(test, sr->sr_extent);
+	KUNIT_ASSERT_PTR_EQ(test, buf, (char *)sr->sr_extent);
+
+	sr->sr_extent[0].off = (__force u64)cpu_to_le64(off);
+	sr->sr_extent[0].len = (__force u64)cpu_to_le64(len);
+	ret = osd_sparse_read(&ctx->con, &ctx->cursor, &buf);
+	KUNIT_ASSERT_EQ(test, ret, (int)sizeof(sr->sr_datalen));
+	KUNIT_ASSERT_PTR_EQ(test, buf, (char *)&sr->sr_datalen);
+
+	sr->sr_datalen = (__force u32)cpu_to_le32(datalen);
+	return osd_sparse_read(&ctx->con, &ctx->cursor, &buf);
+}
+
+static void ceph_osd_sparse_read_in_range_extent(struct kunit *test)
+{
+	struct ceph_osd_sparse_read_test *ctx = test->priv;
+	struct ceph_sparse_read *sr = &ctx->osd.o_sparse_read;
+	int ret;
+
+	ret = ceph_osd_sparse_read_feed_one_extent(test, 0, 16, 16);
+
+	KUNIT_EXPECT_EQ(test, ret, 16);
+	KUNIT_EXPECT_EQ(test, sr->sr_pos, 16);
+	KUNIT_EXPECT_EQ(test, ctx->cursor.sr_resid, 16);
+	KUNIT_EXPECT_EQ(test, ctx->cursor.resid, PAGE_SIZE);
+}
+
+static void ceph_osd_sparse_read_rejects_out_of_range_extent(struct kunit *test)
+{
+	int ret;
+
+	ret = ceph_osd_sparse_read_feed_one_extent(test, PAGE_SIZE + 16, 16, 16);
+
+	KUNIT_EXPECT_EQ(test, ret, -EREMOTEIO);
+}
+
+static struct kunit_case ceph_osd_sparse_read_test_cases[] = {
+	KUNIT_CASE(ceph_osd_sparse_read_in_range_extent),
+	KUNIT_CASE(ceph_osd_sparse_read_rejects_out_of_range_extent),
+	{}
+};
+
+static struct kunit_suite ceph_osd_sparse_read_test_suite = {
+	.name = "ceph_osd_sparse_read",
+	.init = ceph_osd_sparse_read_test_init,
+	.exit = ceph_osd_sparse_read_test_exit,
+	.test_cases = ceph_osd_sparse_read_test_cases,
+};
+
+kunit_test_suite(ceph_osd_sparse_read_test_suite);
diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
index 76ba3abdad9b1..b9ab9608ec88e 100644
--- a/net/ceph/osd_client.c
+++ b/net/ceph/osd_client.c
@@ -5951,3 +5951,7 @@ static const struct ceph_connection_operations osd_con_ops = {
 	.handle_auth_done = osd_handle_auth_done,
 	.handle_auth_bad_method = osd_handle_auth_bad_method,
 };
+
+#ifdef CONFIG_CEPH_LIB_KUNIT_TEST
+#include "osd_client-kunit.c"
+#endif
-- 
2.53.0


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

* Re: [PATCH v2 2/2] libceph: add KUnit coverage for OSD sparse-read extent validation
  2026-07-14 11:51 ` [PATCH v2 2/2] libceph: add KUnit coverage for OSD sparse-read extent validation Michael Bommarito
@ 2026-07-14 17:43   ` Viacheslav Dubeyko
  0 siblings, 0 replies; 4+ messages in thread
From: Viacheslav Dubeyko @ 2026-07-14 17:43 UTC (permalink / raw)
  To: Michael Bommarito, Ilya Dryomov; +Cc: ceph-devel, linux-kernel, stable

On Tue, 2026-07-14 at 07:51 -0400, Michael Bommarito wrote:
> Add KUnit coverage for the sparse-read extent-map validation added by
> the
> previous patch. The tests drive the real osd_sparse_read() state
> machine
> over a synthesized OSD reply (no OSD or network): an in-range single
> extent is accepted and advances the cursor, and an extent whose
> offset
> lies outside the original request range is rejected with -EREMOTEIO
> before
> the message-data cursor is advanced.
> 
> The suite is included from osd_client.c so it can exercise the static
> sparse-read parser and cursor-advance helpers without exporting test-
> only
> symbols, guarded by CONFIG_CEPH_LIB_KUNIT_TEST.
> 
> Assisted-by: Codex:gpt-5-5-xhigh
> Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
> ---
>  net/ceph/Kconfig            |  12 +++
>  net/ceph/osd_client-kunit.c | 146
> ++++++++++++++++++++++++++++++++++++
>  net/ceph/osd_client.c       |   4 +
>  3 files changed, 162 insertions(+)
>  create mode 100644 net/ceph/osd_client-kunit.c
> 
> diff --git a/net/ceph/Kconfig b/net/ceph/Kconfig
> index 7e2528cde4b94..6b78fe4a0f8a4 100644
> --- a/net/ceph/Kconfig
> +++ b/net/ceph/Kconfig
> @@ -45,3 +45,15 @@ config CEPH_LIB_USE_DNS_RESOLVER
>  	  Documentation/networking/dns_resolver.rst
>  
>  	  If unsure, say N.
> +
> +config CEPH_LIB_KUNIT_TEST
> +	bool "KUnit tests for the Ceph core library" if
> !KUNIT_ALL_TESTS
> +	depends on CEPH_LIB && KUNIT
> +	default KUNIT_ALL_TESTS
> +	help
> +	  This builds KUnit coverage for selected Ceph core-library
> parser
> +	  and state-machine helpers. The tests exercise internal
> libceph
> +	  behavior with synthesized state and are intended for
> developer
> +	  validation rather than production systems.
> +
> +	  If unsure, say N.
> diff --git a/net/ceph/osd_client-kunit.c b/net/ceph/osd_client-
> kunit.c
> new file mode 100644
> index 0000000000000..713f24675f68f
> --- /dev/null
> +++ b/net/ceph/osd_client-kunit.c
> @@ -0,0 +1,146 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * KUnit coverage for net/ceph/osd_client.c internals.
> + *
> + * Included from osd_client.c so the test can drive the real static
> sparse-read
> + * parser and cursor-advance helper without exporting test-only
> symbols.
> + */
> +
> +#include <kunit/test.h>
> +
> +struct ceph_osd_sparse_read_test {
> +	struct ceph_osd osd;
> +	struct ceph_connection con;
> +	struct ceph_msg *msg;
> +	struct ceph_msg_data_cursor cursor;
> +	struct ceph_osd_request *req;
> +	struct page **pages;
> +	struct page *page;
> +};
> +
> +static int ceph_osd_sparse_read_test_init(struct kunit *test)
> +{
> +	struct ceph_osd_sparse_read_test *ctx;
> +
> +	ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL);
> +	KUNIT_ASSERT_NOT_NULL(test, ctx);
> +
> +	ctx->req = kunit_kzalloc(test, struct_size(ctx->req, r_ops,
> 1),
> +				 GFP_KERNEL);
> +	KUNIT_ASSERT_NOT_NULL(test, ctx->req);
> +
> +	ctx->pages = kunit_kcalloc(test, 1, sizeof(*ctx->pages),
> GFP_KERNEL);
> +	KUNIT_ASSERT_NOT_NULL(test, ctx->pages);
> +
> +	ctx->page = alloc_page(GFP_KERNEL);
> +	KUNIT_ASSERT_NOT_NULL(test, ctx->page);
> +	ctx->pages[0] = ctx->page;
> +
> +	ctx->msg = ceph_msg_new2(CEPH_MSG_OSD_OPREPLY, 0, 1,
> GFP_KERNEL, true);
> +	KUNIT_ASSERT_NOT_NULL(test, ctx->msg);
> +
> +	osd_init(&ctx->osd);
> +	ctx->osd.o_sparse_op_idx = -1;
> +	ceph_init_sparse_read(&ctx->osd.o_sparse_read);
> +
> +	request_init(ctx->req);
> +	ctx->req->r_tid = 1;
> +	ctx->req->r_num_ops = 1;
> +	osd_req_op_extent_init(ctx->req, 0, CEPH_OSD_OP_SPARSE_READ,
> +			       0, PAGE_SIZE, 0, 0);
> +	KUNIT_ASSERT_EQ(test, ctx->req->r_ops[0].op,
> CEPH_OSD_OP_SPARSE_READ);
> +
> +	insert_request(&ctx->osd.o_requests, ctx->req);
> +
> +	ctx->msg->hdr.tid = cpu_to_le64(ctx->req->r_tid);
> +	ceph_msg_data_add_pages(ctx->msg, ctx->pages, PAGE_SIZE, 0,
> false);
> +	ceph_msg_data_cursor_init(&ctx->cursor, ctx->msg,
> PAGE_SIZE);
> +
> +	ctx->con.private = &ctx->osd;
> +	ctx->con.in_msg = ctx->msg;
> +
> +	test->priv = ctx;
> +	return 0;
> +}
> +
> +static void ceph_osd_sparse_read_test_exit(struct kunit *test)
> +{
> +	struct ceph_osd_sparse_read_test *ctx = test->priv;
> +
> +	if (!ctx)
> +		return;
> +
> +	if (ctx->req && !RB_EMPTY_NODE(&ctx->req->r_node))
> +		erase_request(&ctx->osd.o_requests, ctx->req);
> +	ceph_init_sparse_read(&ctx->osd.o_sparse_read);
> +	if (ctx->msg)
> +		ceph_msg_put(ctx->msg);
> +	if (ctx->page)
> +		__free_page(ctx->page);
> +}
> +
> +static int ceph_osd_sparse_read_feed_one_extent(struct kunit *test,
> +						u64 off, u64 len,
> u32 datalen)
> +{
> +	struct ceph_osd_sparse_read_test *ctx = test->priv;
> +	struct ceph_sparse_read *sr = &ctx->osd.o_sparse_read;
> +	char *buf = NULL;
> +	int ret;
> +
> +	ret = osd_sparse_read(&ctx->con, &ctx->cursor, &buf);
> +	KUNIT_ASSERT_EQ(test, ret, (int)sizeof(sr->sr_count));
> +	KUNIT_ASSERT_PTR_EQ(test, buf, (char *)&sr->sr_count);
> +
> +	sr->sr_count = (__force u32)cpu_to_le32(1);
> +	ret = osd_sparse_read(&ctx->con, &ctx->cursor, &buf);
> +	KUNIT_ASSERT_EQ(test, ret, (int)sizeof(*sr->sr_extent));
> +	KUNIT_ASSERT_NOT_NULL(test, sr->sr_extent);
> +	KUNIT_ASSERT_PTR_EQ(test, buf, (char *)sr->sr_extent);
> +
> +	sr->sr_extent[0].off = (__force u64)cpu_to_le64(off);
> +	sr->sr_extent[0].len = (__force u64)cpu_to_le64(len);
> +	ret = osd_sparse_read(&ctx->con, &ctx->cursor, &buf);
> +	KUNIT_ASSERT_EQ(test, ret, (int)sizeof(sr->sr_datalen));
> +	KUNIT_ASSERT_PTR_EQ(test, buf, (char *)&sr->sr_datalen);
> +
> +	sr->sr_datalen = (__force u32)cpu_to_le32(datalen);
> +	return osd_sparse_read(&ctx->con, &ctx->cursor, &buf);
> +}
> +
> +static void ceph_osd_sparse_read_in_range_extent(struct kunit *test)
> +{
> +	struct ceph_osd_sparse_read_test *ctx = test->priv;
> +	struct ceph_sparse_read *sr = &ctx->osd.o_sparse_read;
> +	int ret;
> +
> +	ret = ceph_osd_sparse_read_feed_one_extent(test, 0, 16, 16);
> +
> +	KUNIT_EXPECT_EQ(test, ret, 16);
> +	KUNIT_EXPECT_EQ(test, sr->sr_pos, 16);
> +	KUNIT_EXPECT_EQ(test, ctx->cursor.sr_resid, 16);
> +	KUNIT_EXPECT_EQ(test, ctx->cursor.resid, PAGE_SIZE);
> +}
> +
> +static void ceph_osd_sparse_read_rejects_out_of_range_extent(struct
> kunit *test)
> +{
> +	int ret;
> +
> +	ret = ceph_osd_sparse_read_feed_one_extent(test, PAGE_SIZE +
> 16, 16, 16);
> +
> +	KUNIT_EXPECT_EQ(test, ret, -EREMOTEIO);
> +}
> +
> +static struct kunit_case ceph_osd_sparse_read_test_cases[] = {
> +	KUNIT_CASE(ceph_osd_sparse_read_in_range_extent),
> +	KUNIT_CASE(ceph_osd_sparse_read_rejects_out_of_range_extent)
> ,
> +	{}
> +};
> +
> +static struct kunit_suite ceph_osd_sparse_read_test_suite = {
> +	.name = "ceph_osd_sparse_read",
> +	.init = ceph_osd_sparse_read_test_init,
> +	.exit = ceph_osd_sparse_read_test_exit,
> +	.test_cases = ceph_osd_sparse_read_test_cases,
> +};
> +
> +kunit_test_suite(ceph_osd_sparse_read_test_suite);
> diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c
> index 76ba3abdad9b1..b9ab9608ec88e 100644
> --- a/net/ceph/osd_client.c
> +++ b/net/ceph/osd_client.c
> @@ -5951,3 +5951,7 @@ static const struct ceph_connection_operations
> osd_con_ops = {
>  	.handle_auth_done = osd_handle_auth_done,
>  	.handle_auth_bad_method = osd_handle_auth_bad_method,
>  };
> +
> +#ifdef CONFIG_CEPH_LIB_KUNIT_TEST
> +#include "osd_client-kunit.c"
> +#endif

Usually, KUnit test requires .kunitconfig file and I don't see it.
Because, there are several ways to run Kunit tests: (1) python script,
(2) running kernel module.

The osd_client-kunit.c should contains:
MODULE_LICENSE()
MODULE_AUTHOR()
MODULE_DESCRIPTION()
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");

The tested function needs to be properly exported:

EXPORT_SYMBOL_IF_KUNIT(<function_name>);

Thanks,
Slava.

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

end of thread, other threads:[~2026-07-14 17:43 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-14 11:51 [PATCH v2 0/2] libceph: validate OSD sparse-read extent maps (+KUnit) Michael Bommarito
2026-07-14 11:51 ` [PATCH v2 1/2] libceph: validate OSD extent maps before cursor advance Michael Bommarito
2026-07-14 11:51 ` [PATCH v2 2/2] libceph: add KUnit coverage for OSD sparse-read extent validation Michael Bommarito
2026-07-14 17:43   ` Viacheslav Dubeyko

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

Powered by JetHome