git: 35b59986b5aa - main - g_part: access leak causes process to hang

From: Warner Losh <imp_at_FreeBSD.org>
Date: Sun, 04 Oct 2026 19:22:18 UTC
The branch main has been updated by imp:

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

commit 35b59986b5aaedbafefbb79e3da30789ca9aeb24
Author:     Warner Losh <imp@FreeBSD.org>
AuthorDate: 2026-10-04 19:19:59 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2026-10-04 19:21:17 +0000

    g_part: access leak causes process to hang
    
    So if one inserts a USB drive with a GPT that doesn't match the media
    size, a resize is initiated, so gpart takes g_access(cp, 1, 1, 1) on the
    disk and holds it until the resize is accepted or rejected. The orphan
    path releases it, but the spoil path does not. So if the drive is
    ejected while the resize is in flight, we call spoil directly, without
    releasing the access. Since gpt_opened is not cleared for the spoil
    path, this causes several different process to hang in the open path
    waiting for the leaked access.
    
    Move the release into the wither function when gpt_opened is set, and
    remove the release elsewhere. Since all paths to destroy the geom pass
    through wither, this ensure that access is always released when we've
    taken the access for resize.
    
    PR:                     297777
    Reported by:            Rick Richard
    MFC After:              1 week
    Sponsored by:           Netflix
    Differential Revision:  https://reviews.freebsd.org/D59138
---
 sys/geom/part/g_part.c | 14 +++-----------
 1 file changed, 3 insertions(+), 11 deletions(-)

diff --git a/sys/geom/part/g_part.c b/sys/geom/part/g_part.c
index 6c4c37d9bfce..cf90429c8643 100644
--- a/sys/geom/part/g_part.c
+++ b/sys/geom/part/g_part.c
@@ -926,7 +926,6 @@ g_part_ctl_commit(struct gctl_req *req, struct g_part_parms *gpp)
 
 	if (table->gpt_scheme == &g_part_null_scheme) {
 		g_topology_lock();
-		g_access(cp, -1, -1, -1);
 		g_part_wither(gp, ENXIO);
 		return (0);
 	}
@@ -1080,7 +1079,6 @@ g_part_ctl_create(struct gctl_req *req, struct g_part_parms *gpp)
 fail:
 	g_topology_lock();
 	if (null == NULL) {
-		g_access(cp, -1, -1, -1);
 		g_part_wither(gp, error);
 	} else {
 		kobj_delete((kobj_t)gp->softc, M_GEOM);
@@ -1529,7 +1527,6 @@ g_part_ctl_undo(struct gctl_req *req, struct g_part_parms *gpp)
 		error = g_part_probe(gp, cp, table->gpt_depth);
 		if (error) {
 			g_topology_lock();
-			g_access(cp, -1, -1, -1);
 			g_part_wither(gp, error);
 			return (0);
 		}
@@ -1577,6 +1574,8 @@ g_part_wither(struct g_geom *gp, int error)
 
 	table = gp->softc;
 	if (table != NULL) {
+		if (table->gpt_opened)
+			g_access(LIST_FIRST(&gp->consumer), -1, -1, -1);
 		gp->softc = NULL;
 		while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
 			LIST_REMOVE(entry, gpe_entry);
@@ -2180,18 +2179,14 @@ g_part_resize(struct g_consumer *cp)
 		    "  Use `gpart commit %s` to save changes or "
 		    "`gpart undo %s` to revert them.\n", cp->geom->name,
 		    cp->geom->name, cp->geom->name);
-	if (g_part_check_integrity(table, cp) != 0) {
-		g_access(cp, -1, -1, -1);
-		table->gpt_opened = 0;
+	if (g_part_check_integrity(table, cp) != 0)
 		g_part_wither(table->gpt_gp, ENXIO);
-	}
 }
 
 static void
 g_part_orphan(struct g_consumer *cp)
 {
 	struct g_provider *pp;
-	struct g_part_table *table;
 
 	pp = cp->provider;
 	KASSERT(pp != NULL, ("%s", __func__));
@@ -2199,9 +2194,6 @@ g_part_orphan(struct g_consumer *cp)
 	g_topology_assert();
 
 	KASSERT(pp->error != 0, ("%s", __func__));
-	table = cp->geom->softc;
-	if (table != NULL && table->gpt_opened)
-		g_access(cp, -1, -1, -1);
 	g_part_wither(cp->geom, pp->error);
 }