aboutsummaryrefslogtreecommitdiff
path: root/cddl
diff options
context:
space:
mode:
authorAlan Somers <asomers@FreeBSD.org>2023-09-12 01:20:39 +0000
committerAlan Somers <asomers@FreeBSD.org>2023-09-21 22:20:43 +0000
commit3676929d3caea131170a48f8e054299d68c81ec5 (patch)
treef511c2369654f648f37f497b985f75bfa741566b /cddl
parentd52a5a3491a76c86bf77f6ce1a801233bc61862c (diff)
Fix zfsd with the device_removal pool feature.
Previously zfsd would crash in the presence of a pool with a top-level-vdev that had previously been removed. The crash happened because the configuration nvlist of such a TLV contains an empty ZPOOL_CONFIG_CHILDREN array, which led to a pop_front from an empty list, which has undefined behavior. The crash only happened in stable/14 and later, probably do to differences in libcxx, but the change should be MFCed anyway. PR: 273663 Reported by: Marek Zarychta <zarychtam@plan-b.pwste.edu.pl> Sponsored by: Axcient Reviewed by: mav Differential Revision: https://reviews.freebsd.org/D41818 (cherry picked from commit 0b294a386d34f6584848ed52407687df7ae59861)
Diffstat (limited to 'cddl')
-rw-r--r--cddl/usr.sbin/zfsd/tests/zfsd_unittest.cc37
-rw-r--r--cddl/usr.sbin/zfsd/vdev_iterator.cc5
2 files changed, 38 insertions, 4 deletions
diff --git a/cddl/usr.sbin/zfsd/tests/zfsd_unittest.cc b/cddl/usr.sbin/zfsd/tests/zfsd_unittest.cc
index f3fea2ca83f4..caeb077a3de8 100644
--- a/cddl/usr.sbin/zfsd/tests/zfsd_unittest.cc
+++ b/cddl/usr.sbin/zfsd/tests/zfsd_unittest.cc
@@ -766,3 +766,40 @@ TEST_F(ReEvaluateByGuidTest, ReEvaluateByGuid_five)
delete CaseFile4;
delete CaseFile5;
}
+
+/*
+ * Test VdevIterator
+ */
+class VdevIteratorTest : public ::testing::Test
+{
+};
+
+bool VdevIteratorTestCB(Vdev &vdev, void *cbArg) {
+ return (false);
+}
+
+/*
+ * VdevIterator::Next should not crash when run on a pool that has a previously
+ * removed vdev. Regression for
+ * https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=273663
+ */
+TEST_F(VdevIteratorTest, VdevRemoval)
+{
+ nvlist_t* poolConfig, *rootVdev;
+
+ ASSERT_EQ(0, nvlist_alloc(&rootVdev, NV_UNIQUE_NAME, 0));
+ ASSERT_EQ(0, nvlist_add_uint64(rootVdev, ZPOOL_CONFIG_GUID, 0x5678));
+ /*
+ * Note: pools with previously-removed top-level VDEVs will contain a
+ * TLV in their labels that has 0 children.
+ */
+ ASSERT_EQ(0, nvlist_add_nvlist_array(rootVdev, ZPOOL_CONFIG_CHILDREN,
+ NULL, 0));
+ ASSERT_EQ(0, nvlist_alloc(&poolConfig, NV_UNIQUE_NAME, 0));
+ ASSERT_EQ(0, nvlist_add_uint64(poolConfig,
+ ZPOOL_CONFIG_POOL_GUID, 0x1234));
+ ASSERT_EQ(0, nvlist_add_nvlist(poolConfig, ZPOOL_CONFIG_VDEV_TREE,
+ rootVdev));
+
+ VdevIterator(poolConfig).Each(VdevIteratorTestCB, NULL);
+}
diff --git a/cddl/usr.sbin/zfsd/vdev_iterator.cc b/cddl/usr.sbin/zfsd/vdev_iterator.cc
index 5a515f91e2ec..e2294856b87d 100644
--- a/cddl/usr.sbin/zfsd/vdev_iterator.cc
+++ b/cddl/usr.sbin/zfsd/vdev_iterator.cc
@@ -100,10 +100,7 @@ VdevIterator::Next()
{
nvlist_t *vdevConfig;
- if (m_vdevQueue.empty())
- return (NULL);
-
- for (;;) {
+ for (vdevConfig = NULL; !m_vdevQueue.empty();) {
nvlist_t **vdevChildren;
int result;
u_int numChildren;