* [PATCH 1/4] NFS: validate pNFS data server port octets
2026-08-23 11:42 [PATCH 0/4] NFS: harden pNFS device address decoding Prabhakar Pujeri
@ 2026-08-23 11:42 ` Prabhakar Pujeri
2026-08-23 11:42 ` [PATCH 2/4] NFS: bound multipath address count in file-layout GETDEVICEINFO Prabhakar Pujeri
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Prabhakar Pujeri @ 2026-08-23 11:42 UTC (permalink / raw)
To: Trond Myklebust, Anna Schumaker; +Cc: linux-nfs, Prabhakar Pujeri, stable
nfs4_decode_mp_ds_addr() parses the two decimal port octets in a
server-provided universal address with an unchecked sscanf(). A missing
or non-numeric field leaves part of the temporary array uninitialized,
while values outside the octet range are silently folded into the
resulting port.
Split the two port fields at their delimiters and parse each with
kstrtou8(). This rejects missing fields, trailing garbage, integer
overflow, and values outside 0 through 255 before constructing the data
server socket address.
Fixes: 6b7f3cf96364 ("nfs41: pull decode_ds_addr from file layout to generic pnfs")
Cc: stable@vger.kernel.org
Signed-off-by: Prabhakar Pujeri <prabhakar.pujeri@dell.com>
---
fs/nfs/pnfs_nfs.c | 47 ++++++++++++++++++++++++++++-------------------
1 file changed, 28 insertions(+), 19 deletions(-)
diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c
index b539e1a44d26..0c8c50eab4ad 100644
--- a/fs/nfs/pnfs_nfs.c
+++ b/fs/nfs/pnfs_nfs.c
@@ -8,6 +8,7 @@
* Tom Haynes <loghyr@primarydata.com>
*/
+#include <linux/ctype.h>
#include <linux/nfs_fs.h>
#include <linux/nfs_page.h>
#include <linux/sunrpc/addr.h>
@@ -1059,6 +1060,19 @@ int nfs4_pnfs_ds_connect(struct nfs_server *mds_srv, struct nfs4_pnfs_ds *ds,
}
EXPORT_SYMBOL_GPL(nfs4_pnfs_ds_connect);
+static int
+nfs4_parse_port_octet(const char *str, u8 *port)
+{
+ const char *p;
+
+ if (!*str)
+ return -EINVAL;
+ for (p = str; *p; p++)
+ if (!isdigit(*p))
+ return -EINVAL;
+ return kstrtou8(str, 10, port);
+}
+
/*
* Currently only supports ipv4, ipv6 and one multi-path address.
*/
@@ -1066,10 +1080,10 @@ struct nfs4_pnfs_ds_addr *
nfs4_decode_mp_ds_addr(struct net *net, struct xdr_stream *xdr, gfp_t gfp_flags)
{
struct nfs4_pnfs_ds_addr *da = NULL;
- char *buf, *portstr;
+ char *buf, *portsep;
__be16 port;
ssize_t nlen, rlen;
- int tmp[2];
+ u8 porthi, portlo;
char *netid;
size_t len;
char *startsep = "";
@@ -1089,37 +1103,33 @@ nfs4_decode_mp_ds_addr(struct net *net, struct xdr_stream *xdr, gfp_t gfp_flags)
if (unlikely(rlen <= 0))
goto out_free_netid;
- /* replace port '.' with '-' */
- portstr = strrchr(buf, '.');
- if (!portstr) {
- dprintk("%s: Failed finding expected dot in port\n",
- __func__);
+ /* Parse the low port octet and remove it from the address string. */
+ portsep = strrchr(buf, '.');
+ if (!portsep || nfs4_parse_port_octet(portsep + 1, &portlo)) {
+ dprintk("%s: Failed parsing low port octet\n", __func__);
goto out_free_buf;
}
- *portstr = '-';
+ *portsep = '\0';
- /* find '.' between address and port */
- portstr = strrchr(buf, '.');
- if (!portstr) {
- dprintk("%s: Failed finding expected dot between address and "
- "port\n", __func__);
+ /* Parse the high port octet and terminate the address string. */
+ portsep = strrchr(buf, '.');
+ if (!portsep || nfs4_parse_port_octet(portsep + 1, &porthi)) {
+ dprintk("%s: Failed parsing high port octet\n", __func__);
goto out_free_buf;
}
- *portstr = '\0';
+ *portsep = '\0';
da = nfs4_pnfs_ds_addr_alloc(gfp_flags);
if (unlikely(!da))
goto out_free_buf;
- if (!rpc_pton(net, buf, portstr-buf, (struct sockaddr *)&da->da_addr,
+ if (!rpc_pton(net, buf, portsep - buf, (struct sockaddr *)&da->da_addr,
sizeof(da->da_addr))) {
dprintk("%s: error parsing address %s\n", __func__, buf);
goto out_free_da;
}
- portstr++;
- sscanf(portstr, "%d-%d", &tmp[0], &tmp[1]);
- port = htons((tmp[0] << 8) | (tmp[1]));
+ port = htons((porthi << 8) | portlo);
switch (da->da_addr.ss_family) {
case AF_INET:
@@ -1226,4 +1236,3 @@ pnfs_nfs_generic_sync(struct inode *inode, bool datasync)
return pnfs_layoutcommit_inode(inode, true);
}
EXPORT_SYMBOL_GPL(pnfs_nfs_generic_sync);
-
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH 4/4] NFS: add KUnit coverage for pNFS address decoding
2026-08-23 11:42 [PATCH 0/4] NFS: harden pNFS device address decoding Prabhakar Pujeri
` (2 preceding siblings ...)
2026-08-23 11:42 ` [PATCH 3/4] NFS: bound multipath address count in flexfiles GETDEVICEINFO Prabhakar Pujeri
@ 2026-08-23 11:42 ` Prabhakar Pujeri
3 siblings, 0 replies; 5+ messages in thread
From: Prabhakar Pujeri @ 2026-08-23 11:42 UTC (permalink / raw)
To: Trond Myklebust, Anna Schumaker; +Cc: linux-nfs, Prabhakar Pujeri
Add page-backed XDR tests for nfs4_decode_mp_ds_addr(). Cover valid IPv4
and IPv6 universal addresses, boundary port values, missing fields,
non-numeric fields, out-of-range values, integer overflow, and trailing
garbage.
Add a separate flexfiles GETDEVICEINFO test module that exercises a valid
single-address payload and verifies that zero and U32_MAX multipath
counts are rejected. Export the two flexfiles decoder helpers only when
KUnit is enabled.
Keep both suites behind guideline-compliant Kconfig entries that default
with KUNIT_ALL_TESTS and can be built as modules. Manage the flexfiles
fixtures and decoded devices as KUnit resources so assertion failures do
not leak them.
Signed-off-by: Prabhakar Pujeri <prabhakar.pujeri@dell.com>
---
fs/nfs/Kconfig | 34 +++
fs/nfs/Makefile | 3 +
fs/nfs/flexfilelayout/Makefile | 3 +
fs/nfs/flexfilelayout/flexfilelayoutdev.c | 4 +
fs/nfs/flexfilelayout/tests/deviceid_kunit.c | 188 ++++++++++++++++
fs/nfs/tests/pnfs_decode_kunit.c | 222 +++++++++++++++++++
6 files changed, 454 insertions(+)
create mode 100644 fs/nfs/flexfilelayout/tests/deviceid_kunit.c
create mode 100644 fs/nfs/tests/pnfs_decode_kunit.c
diff --git a/fs/nfs/Kconfig b/fs/nfs/Kconfig
index 6bb30543eff0..0d7a01f7e10b 100644
--- a/fs/nfs/Kconfig
+++ b/fs/nfs/Kconfig
@@ -215,3 +215,37 @@ config NFS_V4_2_READ_PLUS
default y
help
Choose Y here to enable use of the NFS v4.2 READ_PLUS operation.
+
+config NFS_PNFS_DECODE_KUNIT_TEST
+ tristate "KUnit tests for pNFS data server address decoding" if !KUNIT_ALL_TESTS
+ depends on NFS_V4 && KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds KUnit tests for the pNFS data server address decoder.
+ The tests cover valid IPv4 and IPv6 universal addresses and malformed
+ port fields supplied by an NFS server. They verify that invalid port
+ octets cannot produce a usable data server socket address.
+ Results are reported in TAP format when the test module is loaded on a
+ KUnit-enabled kernel.
+
+ For more information on KUnit and unit tests in general, refer to
+ Documentation/dev-tools/kunit/.
+
+ If unsure, say N.
+
+config NFS_FLEXFILE_DEVICEID_KUNIT_TEST
+ tristate "KUnit tests for flexfiles GETDEVICEINFO decoding" if !KUNIT_ALL_TESTS
+ depends on PNFS_FLEXFILE_LAYOUT && KUNIT
+ default KUNIT_ALL_TESTS
+ help
+ This builds KUnit tests for the flexfiles GETDEVICEINFO decoder.
+ The tests cover valid device information and malformed multipath
+ address counts supplied by an NFS server. They verify that oversized
+ counts are rejected before entering the address decode loop.
+ Results are reported in TAP format when the test module is loaded on a
+ KUnit-enabled kernel.
+
+ For more information on KUnit and unit tests in general, refer to
+ Documentation/dev-tools/kunit/.
+
+ If unsure, say N.
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
index c895521f27f3..e68648f236b0 100644
--- a/fs/nfs/Makefile
+++ b/fs/nfs/Makefile
@@ -33,6 +33,9 @@ nfsv4-$(CONFIG_SYSCTL) += nfs4sysctl.o
nfsv4-$(CONFIG_NFS_V4_0) += nfs40client.o nfs40proc.o
nfsv4-$(CONFIG_NFS_V4_2) += nfs42proc.o nfs42xattr.o
+obj-$(CONFIG_NFS_PNFS_DECODE_KUNIT_TEST) += nfs_pnfs_decode_kunit.o
+nfs_pnfs_decode_kunit-y := tests/pnfs_decode_kunit.o
+
obj-$(CONFIG_PNFS_FILE_LAYOUT) += filelayout/
obj-$(CONFIG_PNFS_BLOCK) += blocklayout/
obj-$(CONFIG_PNFS_FLEXFILE_LAYOUT) += flexfilelayout/
diff --git a/fs/nfs/flexfilelayout/Makefile b/fs/nfs/flexfilelayout/Makefile
index 49f03422b6ad..8dcc8fad4106 100644
--- a/fs/nfs/flexfilelayout/Makefile
+++ b/fs/nfs/flexfilelayout/Makefile
@@ -4,3 +4,6 @@
#
obj-$(CONFIG_PNFS_FLEXFILE_LAYOUT) += nfs_layout_flexfiles.o
nfs_layout_flexfiles-y := flexfilelayout.o flexfilelayoutdev.o
+
+obj-$(CONFIG_NFS_FLEXFILE_DEVICEID_KUNIT_TEST) += nfs_flexfile_deviceid_kunit.o
+nfs_flexfile_deviceid_kunit-y := tests/deviceid_kunit.o
diff --git a/fs/nfs/flexfilelayout/flexfilelayoutdev.c b/fs/nfs/flexfilelayout/flexfilelayoutdev.c
index 181a5854fa37..1979a8017ad3 100644
--- a/fs/nfs/flexfilelayout/flexfilelayoutdev.c
+++ b/fs/nfs/flexfilelayout/flexfilelayoutdev.c
@@ -12,6 +12,8 @@
#include <linux/module.h>
#include <linux/sunrpc/addr.h>
+#include <kunit/visibility.h>
+
#include "../internal.h"
#include "../nfs4session.h"
#include "flexfilelayout.h"
@@ -36,6 +38,7 @@ void nfs4_ff_layout_free_deviceid(struct nfs4_ff_layout_ds *mirror_ds)
kfree(mirror_ds->ds_versions);
kfree_rcu(mirror_ds, id_node.rcu);
}
+EXPORT_SYMBOL_IF_KUNIT(nfs4_ff_layout_free_deviceid);
/* Decode opaque device data and construct new_ds using it */
struct nfs4_ff_layout_ds *
@@ -193,6 +196,7 @@ nfs4_ff_alloc_deviceid_node(struct nfs_server *server, struct pnfs_device *pdev,
dprintk("%s ERROR: returning %d\n", __func__, ret);
return NULL;
}
+EXPORT_SYMBOL_IF_KUNIT(nfs4_ff_alloc_deviceid_node);
static void extend_ds_error(struct nfs4_ff_layout_ds_err *err,
u64 offset, u64 length)
diff --git a/fs/nfs/flexfilelayout/tests/deviceid_kunit.c b/fs/nfs/flexfilelayout/tests/deviceid_kunit.c
new file mode 100644
index 000000000000..d45fb80fd179
--- /dev/null
+++ b/fs/nfs/flexfilelayout/tests/deviceid_kunit.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit tests for the NFS flexfiles GETDEVICEINFO decoder.
+ */
+
+#include <kunit/test.h>
+#include <linux/mm.h>
+#include <net/net_namespace.h>
+
+#include "../flexfilelayout.h"
+
+static void flexfile_deviceid_put_u32(char **p, u32 value)
+{
+ *(__be32 *)*p = cpu_to_be32(value);
+ *p += XDR_UNIT;
+}
+
+static void flexfile_deviceid_put_string(char **p, const char *string)
+{
+ size_t len = strlen(string);
+
+ flexfile_deviceid_put_u32(p, len);
+ memcpy(*p, string, len);
+ *p += xdr_align_size(len);
+}
+
+static struct nfs_server *flexfile_deviceid_server(struct kunit *test)
+{
+ struct nfs_server *server;
+ struct nfs_client *client;
+
+ server = kunit_kzalloc(test, sizeof(*server), GFP_KERNEL);
+ if (!server)
+ return NULL;
+ client = kunit_kzalloc(test, sizeof(*client), GFP_KERNEL);
+ if (!client)
+ return NULL;
+
+ client->cl_net = &init_net;
+ client->cl_proto = IPPROTO_TCP;
+ server->nfs_client = client;
+ return server;
+}
+
+static struct pnfs_device *
+flexfile_deviceid_pdev(struct kunit *test, struct page *page, size_t len)
+{
+ struct pnfs_device *pdev;
+
+ pdev = kunit_kzalloc(test, sizeof(*pdev), GFP_KERNEL);
+ if (!pdev)
+ return NULL;
+ pdev->pages = kunit_kzalloc(test, sizeof(*pdev->pages), GFP_KERNEL);
+ if (!pdev->pages)
+ return NULL;
+ pdev->pages[0] = page;
+ pdev->pglen = len;
+ return pdev;
+}
+
+static void flexfile_deviceid_free_page(void *data)
+{
+ __free_page(data);
+}
+
+static struct page *flexfile_deviceid_page(struct kunit *test)
+{
+ struct page *page;
+
+ page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (!page)
+ return NULL;
+ if (kunit_add_action_or_reset(test, flexfile_deviceid_free_page,
+ page))
+ return NULL;
+ return page;
+}
+
+static void flexfile_deviceid_free(void *data)
+{
+ nfs4_ff_layout_free_deviceid(data);
+}
+
+static int
+flexfile_deviceid_track(struct kunit *test, struct nfs4_ff_layout_ds *device)
+{
+ if (!device)
+ return 0;
+ return kunit_add_action_or_reset(test, flexfile_deviceid_free, device);
+}
+
+static void flexfile_deviceid_reject_huge_multipath_count(struct kunit *test)
+{
+ struct nfs4_ff_layout_ds *device;
+ struct nfs_server *server;
+ struct pnfs_device *pdev;
+ struct page *page;
+ char *start, *p;
+
+ server = flexfile_deviceid_server(test);
+ KUNIT_ASSERT_NOT_NULL(test, server);
+ page = flexfile_deviceid_page(test);
+ KUNIT_ASSERT_NOT_NULL(test, page);
+ start = page_address(page);
+ p = start;
+ flexfile_deviceid_put_u32(&p, U32_MAX);
+ pdev = flexfile_deviceid_pdev(test, page, p - start);
+ KUNIT_ASSERT_NOT_NULL(test, pdev);
+
+ device = nfs4_ff_alloc_deviceid_node(server, pdev, GFP_KERNEL);
+ KUNIT_ASSERT_EQ(test, flexfile_deviceid_track(test, device), 0);
+ KUNIT_EXPECT_NULL(test, device);
+}
+
+static void flexfile_deviceid_reject_zero_multipath_count(struct kunit *test)
+{
+ struct nfs4_ff_layout_ds *device;
+ struct nfs_server *server;
+ struct pnfs_device *pdev;
+ struct page *page;
+ char *start, *p;
+
+ server = flexfile_deviceid_server(test);
+ KUNIT_ASSERT_NOT_NULL(test, server);
+ page = flexfile_deviceid_page(test);
+ KUNIT_ASSERT_NOT_NULL(test, page);
+ start = page_address(page);
+ p = start;
+ flexfile_deviceid_put_u32(&p, 0);
+ pdev = flexfile_deviceid_pdev(test, page, p - start);
+ KUNIT_ASSERT_NOT_NULL(test, pdev);
+
+ device = nfs4_ff_alloc_deviceid_node(server, pdev, GFP_KERNEL);
+ KUNIT_ASSERT_EQ(test, flexfile_deviceid_track(test, device), 0);
+ KUNIT_EXPECT_NULL(test, device);
+}
+
+static void flexfile_deviceid_decode_single_address(struct kunit *test)
+{
+ struct nfs4_ff_layout_ds *device;
+ struct nfs_server *server;
+ struct pnfs_device *pdev;
+ struct page *page;
+ char *start, *p;
+
+ server = flexfile_deviceid_server(test);
+ KUNIT_ASSERT_NOT_NULL(test, server);
+ page = flexfile_deviceid_page(test);
+ KUNIT_ASSERT_NOT_NULL(test, page);
+ start = page_address(page);
+ p = start;
+ flexfile_deviceid_put_u32(&p, 1);
+ flexfile_deviceid_put_string(&p, "tcp");
+ flexfile_deviceid_put_string(&p, "10.0.0.9.8.1");
+ flexfile_deviceid_put_u32(&p, 1);
+ flexfile_deviceid_put_u32(&p, 4);
+ flexfile_deviceid_put_u32(&p, 2);
+ flexfile_deviceid_put_u32(&p, 1048576);
+ flexfile_deviceid_put_u32(&p, 1048576);
+ flexfile_deviceid_put_u32(&p, 0);
+ pdev = flexfile_deviceid_pdev(test, page, p - start);
+ KUNIT_ASSERT_NOT_NULL(test, pdev);
+
+ device = nfs4_ff_alloc_deviceid_node(server, pdev, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, device);
+ KUNIT_ASSERT_EQ(test, flexfile_deviceid_track(test, device), 0);
+ KUNIT_EXPECT_EQ(test, device->ds_versions_cnt, (u32)1);
+ KUNIT_EXPECT_EQ(test, device->ds_versions[0].version, (u32)4);
+ KUNIT_EXPECT_EQ(test, device->ds_versions[0].minor_version, (u32)2);
+}
+
+static struct kunit_case flexfile_deviceid_test_cases[] = {
+ KUNIT_CASE(flexfile_deviceid_reject_huge_multipath_count),
+ KUNIT_CASE(flexfile_deviceid_reject_zero_multipath_count),
+ KUNIT_CASE(flexfile_deviceid_decode_single_address),
+ {}
+};
+
+static struct kunit_suite flexfile_deviceid_test_suite = {
+ .name = "nfs_flexfile_deviceid",
+ .test_cases = flexfile_deviceid_test_cases,
+};
+
+kunit_test_suite(flexfile_deviceid_test_suite);
+
+MODULE_DESCRIPTION("KUnit tests for flexfiles GETDEVICEINFO decoding");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
diff --git a/fs/nfs/tests/pnfs_decode_kunit.c b/fs/nfs/tests/pnfs_decode_kunit.c
new file mode 100644
index 000000000000..74ffe14a089c
--- /dev/null
+++ b/fs/nfs/tests/pnfs_decode_kunit.c
@@ -0,0 +1,222 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit tests for pNFS data server address decoding.
+ */
+
+#include <kunit/test.h>
+#include <linux/mm.h>
+#include <linux/sunrpc/xdr.h>
+#include <net/net_namespace.h>
+
+#include "../pnfs.h"
+
+static struct page *
+pnfs_decode_make_stream(const char *netid, const char *addr,
+ struct xdr_stream *xdr, struct xdr_buf *buf,
+ struct page **pages)
+{
+ size_t netid_len = strlen(netid);
+ size_t addr_len = strlen(addr);
+ struct page *page;
+ char *p;
+ size_t offset = 0;
+
+ if (XDR_UNIT * 2 + xdr_align_size(netid_len) +
+ xdr_align_size(addr_len) > PAGE_SIZE)
+ return ERR_PTR(-E2BIG);
+
+ page = alloc_page(GFP_KERNEL | __GFP_ZERO);
+ if (!page)
+ return ERR_PTR(-ENOMEM);
+ pages[0] = page;
+ p = page_address(page);
+
+ *(__be32 *)(p + offset) = cpu_to_be32(netid_len);
+ offset += XDR_UNIT;
+ memcpy(p + offset, netid, netid_len);
+ offset += xdr_align_size(netid_len);
+ *(__be32 *)(p + offset) = cpu_to_be32(addr_len);
+ offset += XDR_UNIT;
+ memcpy(p + offset, addr, addr_len);
+ offset += xdr_align_size(addr_len);
+
+ xdr_init_decode_pages(xdr, buf, pages, offset);
+ return page;
+}
+
+static void pnfs_decode_free_addr(struct nfs4_pnfs_ds_addr *addr)
+{
+ kfree(addr->da_remotestr);
+ kfree(addr->da_netid);
+ kfree(addr);
+}
+
+static struct nfs4_pnfs_ds_addr *
+pnfs_decode_addr(const char *netid, const char *addr)
+{
+ struct page *pages[1] = { NULL };
+ struct xdr_buf buf = {};
+ struct xdr_stream xdr;
+ struct nfs4_pnfs_ds_addr *decoded;
+ struct page *page;
+
+ page = pnfs_decode_make_stream(netid, addr, &xdr, &buf, pages);
+ if (IS_ERR(page))
+ return ERR_CAST(page);
+
+ decoded = nfs4_decode_mp_ds_addr(&init_net, &xdr, GFP_KERNEL);
+ xdr_finish_decode(&xdr);
+ __free_page(page);
+ return decoded;
+}
+
+static void pnfs_decode_valid_ipv4(struct kunit *test)
+{
+ struct nfs4_pnfs_ds_addr *addr;
+ struct sockaddr_in *sin;
+
+ addr = pnfs_decode_addr("tcp", "10.0.0.4.8.1");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, addr);
+ sin = (struct sockaddr_in *)&addr->da_addr;
+ KUNIT_EXPECT_EQ(test, sin->sin_family, AF_INET);
+ KUNIT_EXPECT_EQ(test, ntohs(sin->sin_port), 2049);
+ KUNIT_EXPECT_EQ(test, be32_to_cpu(sin->sin_addr.s_addr), 0x0a000004);
+ pnfs_decode_free_addr(addr);
+}
+
+static void pnfs_decode_valid_ipv4_max_port(struct kunit *test)
+{
+ struct nfs4_pnfs_ds_addr *addr;
+ struct sockaddr_in *sin;
+
+ addr = pnfs_decode_addr("tcp", "10.0.0.4.255.255");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, addr);
+ sin = (struct sockaddr_in *)&addr->da_addr;
+ KUNIT_EXPECT_EQ(test, ntohs(sin->sin_port), 65535);
+ pnfs_decode_free_addr(addr);
+}
+
+static void pnfs_decode_valid_ipv4_zero_port(struct kunit *test)
+{
+ struct nfs4_pnfs_ds_addr *addr;
+ struct sockaddr_in *sin;
+
+ addr = pnfs_decode_addr("tcp", "10.0.0.4.0.0");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, addr);
+ sin = (struct sockaddr_in *)&addr->da_addr;
+ KUNIT_EXPECT_EQ(test, ntohs(sin->sin_port), 0);
+ pnfs_decode_free_addr(addr);
+}
+
+static void pnfs_decode_valid_ipv6(struct kunit *test)
+{
+ struct nfs4_pnfs_ds_addr *addr;
+ struct sockaddr_in6 *sin6;
+
+ addr = pnfs_decode_addr("tcp6", "2001:db8::5.8.1");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, addr);
+ sin6 = (struct sockaddr_in6 *)&addr->da_addr;
+ KUNIT_EXPECT_EQ(test, sin6->sin6_family, AF_INET6);
+ KUNIT_EXPECT_EQ(test, ntohs(sin6->sin6_port), 2049);
+ pnfs_decode_free_addr(addr);
+}
+
+static void pnfs_decode_expect_invalid(struct kunit *test, const char *addr)
+{
+ struct nfs4_pnfs_ds_addr *decoded = pnfs_decode_addr("tcp", addr);
+
+ KUNIT_ASSERT_FALSE(test, IS_ERR(decoded));
+ KUNIT_EXPECT_NULL(test, decoded);
+ if (decoded)
+ pnfs_decode_free_addr(decoded);
+}
+
+static void pnfs_decode_reject_high_octet_over_255(struct kunit *test)
+{
+ pnfs_decode_expect_invalid(test, "10.0.0.4.300.1");
+}
+
+static void pnfs_decode_reject_low_octet_over_255(struct kunit *test)
+{
+ pnfs_decode_expect_invalid(test, "10.0.0.4.1.300");
+}
+
+static void pnfs_decode_reject_integer_overflow(struct kunit *test)
+{
+ pnfs_decode_expect_invalid(test, "10.0.0.4.999999999999999999999.1");
+}
+
+static void pnfs_decode_reject_empty_low_octet(struct kunit *test)
+{
+ pnfs_decode_expect_invalid(test, "10.0.0.4.8.");
+}
+
+static void pnfs_decode_reject_empty_high_octet(struct kunit *test)
+{
+ pnfs_decode_expect_invalid(test, "10.0.0.4..1");
+}
+
+static void pnfs_decode_reject_negative_octet(struct kunit *test)
+{
+ pnfs_decode_expect_invalid(test, "10.0.0.4.8.-1");
+}
+
+static void pnfs_decode_reject_leading_plus(struct kunit *test)
+{
+ pnfs_decode_expect_invalid(test, "10.0.0.4.+8.1");
+}
+
+static void pnfs_decode_reject_non_numeric_octet(struct kunit *test)
+{
+ pnfs_decode_expect_invalid(test, "10.0.0.4.8.x");
+}
+
+static void pnfs_decode_reject_trailing_garbage(struct kunit *test)
+{
+ pnfs_decode_expect_invalid(test, "10.0.0.4.8.1x");
+}
+
+static void pnfs_decode_reject_trailing_newline(struct kunit *test)
+{
+ pnfs_decode_expect_invalid(test, "10.0.0.4.8.1\n");
+}
+
+static void pnfs_decode_reject_bad_address(struct kunit *test)
+{
+ pnfs_decode_expect_invalid(test, "999.1.1.1.8.1");
+}
+
+static void pnfs_decode_reject_missing_port_octet(struct kunit *test)
+{
+ pnfs_decode_expect_invalid(test, "10.0.0.4.8");
+}
+
+static struct kunit_case pnfs_decode_test_cases[] = {
+ KUNIT_CASE(pnfs_decode_valid_ipv4),
+ KUNIT_CASE(pnfs_decode_valid_ipv4_max_port),
+ KUNIT_CASE(pnfs_decode_valid_ipv4_zero_port),
+ KUNIT_CASE(pnfs_decode_valid_ipv6),
+ KUNIT_CASE(pnfs_decode_reject_high_octet_over_255),
+ KUNIT_CASE(pnfs_decode_reject_low_octet_over_255),
+ KUNIT_CASE(pnfs_decode_reject_integer_overflow),
+ KUNIT_CASE(pnfs_decode_reject_empty_low_octet),
+ KUNIT_CASE(pnfs_decode_reject_empty_high_octet),
+ KUNIT_CASE(pnfs_decode_reject_negative_octet),
+ KUNIT_CASE(pnfs_decode_reject_leading_plus),
+ KUNIT_CASE(pnfs_decode_reject_non_numeric_octet),
+ KUNIT_CASE(pnfs_decode_reject_trailing_garbage),
+ KUNIT_CASE(pnfs_decode_reject_trailing_newline),
+ KUNIT_CASE(pnfs_decode_reject_bad_address),
+ KUNIT_CASE(pnfs_decode_reject_missing_port_octet),
+ {}
+};
+
+static struct kunit_suite pnfs_decode_test_suite = {
+ .name = "nfs_pnfs_decode",
+ .test_cases = pnfs_decode_test_cases,
+};
+
+kunit_test_suite(pnfs_decode_test_suite);
+
+MODULE_DESCRIPTION("KUnit tests for pNFS data server address decoding");
+MODULE_LICENSE("GPL");
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread