git: b2dff90c0be7 - stable/12 - Fix zfsd with the device_removal pool feature.

From: Alan Somers <asomers_at_FreeBSD.org>
Date: Thu, 21 Sep 2023 22:27:21 UTC
The branch stable/12 has been updated by asomers:

URL: https://cgit.FreeBSD.org/src/commit/?id=b2dff90c0be7c92a228fdbee5f69335dcc8044fb

commit b2dff90c0be7c92a228fdbee5f69335dcc8044fb
Author:     Alan Somers <asomers@FreeBSD.org>
AuthorDate: 2023-09-12 01:20:39 +0000
Commit:     Alan Somers <asomers@FreeBSD.org>
CommitDate: 2023-09-21 22:26:29 +0000

    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)
---
 cddl/usr.sbin/zfsd/tests/zfsd_unittest.cc | 37 +++++++++++++++++++++++++++++++
 cddl/usr.sbin/zfsd/vdev_iterator.cc       |  5 +----
 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 d65295075c1f..b4f1cecca033 100644
--- a/cddl/usr.sbin/zfsd/tests/zfsd_unittest.cc
+++ b/cddl/usr.sbin/zfsd/tests/zfsd_unittest.cc
@@ -768,3 +768,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 31a4ce962970..6e60e13eeeb7 100644
--- a/cddl/usr.sbin/zfsd/vdev_iterator.cc
+++ b/cddl/usr.sbin/zfsd/vdev_iterator.cc
@@ -101,10 +101,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;