Add DM tests that feed nfs_pkt_recv() crafted NFSv3 replies with a read and
a readlink request outstanding. The READ reply carries a count with the top
bit set; the READLINK reply carries a length of -1 that slips past the
destination bound as pathlen - 1. Either would drive a memcpy() out of the
reply buffer; the tests assert that nothing is stored and the path buffer is
left untouched.

Enable CONFIG_CMD_NFS in sandbox_defconfig so the NFS client and these tests
are built and run under sandbox.

Signed-off-by: Shahriyar Jalayeri <[email protected]>
---
 configs/sandbox_defconfig |  1 +
 test/dm/Makefile          |  1 +
 test/dm/nfs.c             | 90 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 92 insertions(+)

diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index 79f46317e45..ca73080b06d 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -123,6 +123,7 @@ CONFIG_CMD_LINK_LOCAL=y
 CONFIG_IPV6_ROUTER_DISCOVERY=y
 CONFIG_CMD_ETHSW=y
 CONFIG_CMD_DNS=y
+CONFIG_CMD_NFS=y
 CONFIG_CMD_SNTP=y
 CONFIG_CMD_2048=y
 CONFIG_CMD_BMP=y
diff --git a/test/dm/Makefile b/test/dm/Makefile
index fb3e6a7008f..cc51fd33079 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -78,6 +78,7 @@ obj-$(CONFIG_MUX_MMIO) += mux-mmio.o
 obj-y += fdtdec.o
 obj-$(CONFIG_MTD_RAW_NAND) += nand.o
 obj-$(CONFIG_IP_DEFRAG) += net_defrag.o
+obj-$(CONFIG_CMD_NFS) += nfs.o
 obj-$(CONFIG_UT_DM) += nop.o
 obj-y += ofnode.o
 obj-y += ofread.o
diff --git a/test/dm/nfs.c b/test/dm/nfs.c
new file mode 100644
index 00000000000..c7ab1e912b8
--- /dev/null
+++ b/test/dm/nfs.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Regression tests for the NFS reply-length checks.
+ */
+
+#include <net.h>
+#include <string.h>
+#include <test/ut.h>
+#include <dm/test.h>
+#include "../../net/nfs-common.h"
+
+static int dm_test_nfs_read_oob(struct unit_test_state *uts)
+{
+       int saved_state = nfs_state;
+       unsigned long saved_id = rpc_id;
+       int saved_offset = nfs_offset;
+       enum nfs_version saved_version = choosen_nfs_version;
+       u32 saved_size = net_boot_file_size;
+       struct rpc_t reply;
+
+       /* Pretend a READ request is outstanding (NFSv3). */
+       choosen_nfs_version = NFS_V3;
+       nfs_state = STATE_READ_REQ;
+       nfs_offset = 0;
+       rpc_id = 0x11223344;
+       net_boot_file_size = 0;
+
+       /* Accepted reply, matching xid, READ status OK, no attributes, then a
+        * count with the top bit set.
+        */
+       memset(&reply, 0, sizeof(reply));
+       reply.u.reply.id = htonl((u32)rpc_id);
+       reply.u.reply.data[0] = 0;                      /* nfsstat3: OK */
+       reply.u.reply.data[1] = 0;                      /* attributes_follow: 
no */
+       reply.u.reply.data[2] = htonl(0x80000000);      /* count */
+
+       nfs_pkt_recv((uchar *)&reply.u.reply, sizeof(reply.u.reply));
+
+       /* Rejected: nothing stored. */
+       ut_asserteq(0, net_boot_file_size);
+
+       nfs_state = saved_state;
+       rpc_id = saved_id;
+       nfs_offset = saved_offset;
+       choosen_nfs_version = saved_version;
+       net_boot_file_size = saved_size;
+
+       return 0;
+}
+DM_TEST(dm_test_nfs_read_oob, 0);
+
+static int dm_test_nfs_readlink_oob(struct unit_test_state *uts)
+{
+       int saved_state = nfs_state;
+       unsigned long saved_id = rpc_id;
+       enum nfs_version saved_version = choosen_nfs_version;
+       char *saved_path = nfs_path;
+       struct rpc_t reply;
+
+       /* Pretend a READLINK request is outstanding (NFSv3). */
+       choosen_nfs_version = NFS_V3;
+       nfs_state = STATE_READLINK_REQ;
+       rpc_id = 0x11223344;
+       nfs_path = nfs_path_buff;
+       strcpy(nfs_path_buff, "dir");
+
+       /* Accepted reply, matching xid, READLINK status OK, no attributes, a
+        * length of -1 that slips past the destination bound as pathlen - 1,
+        * then a relative (non-'/') target.
+        */
+       memset(&reply, 0, sizeof(reply));
+       reply.u.reply.id = htonl((u32)rpc_id);
+       reply.u.reply.data[0] = 0;                      /* nfsstat3: OK */
+       reply.u.reply.data[1] = 0;                      /* attributes_follow: 
no */
+       reply.u.reply.data[2] = htonl(0xffffffff);      /* symlink length -1 */
+       reply.u.reply.data[3] = htonl(0x61616161);      /* target, not '/' */
+
+       nfs_pkt_recv((uchar *)&reply.u.reply, sizeof(reply.u.reply));
+
+       /* Rejected: the path buffer is untouched. */
+       ut_asserteq_str("dir", nfs_path_buff);
+
+       nfs_state = saved_state;
+       rpc_id = saved_id;
+       choosen_nfs_version = saved_version;
+       nfs_path = saved_path;
+
+       return 0;
+}
+DM_TEST(dm_test_nfs_readlink_oob, 0);

-- 
2.43.0

Reply via email to