DragonFlyBSD Kernel Audit
DF-1043 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/bus/u4b/serial/ufoma.c b/sys/bus/u4b/serial/ufoma.c
--- a/sys/bus/u4b/serial/ufoma.c
+++ b/sys/bus/u4b/serial/ufoma.c
@@ -185,6 +185,11 @@
 	uint8_t	sc_modetoactivate;
 	uint8_t	sc_currentmode;
 	uint8_t	sc_name[16];
+
+	/* sysctl OIDs — removed in ufoma_detach before sc_modetable is freed */
+	struct sysctl_oid *sc_oid_support;
+	struct sysctl_oid *sc_oid_current;
+	struct sysctl_oid *sc_oid_open;
 };
 
 /* prototypes */
@@ -450,15 +455,18 @@
 	sctx = device_get_sysctl_ctx(dev);
 	soid = device_get_sysctl_tree(dev);
 
-	SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "supportmode",
+	sc->sc_oid_support = SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid),
+			OID_AUTO, "supportmode",
 			CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_support,
 			"A", "Supporting port role");
 
-	SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "currentmode",
+	sc->sc_oid_current = SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid),
+			OID_AUTO, "currentmode",
 			CTLFLAG_RD|CTLTYPE_STRING, sc, 0, ufoma_sysctl_current,
 			"A", "Current port role");
 
-	SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "openmode",
+	sc->sc_oid_open = SYSCTL_ADD_PROC(sctx, SYSCTL_CHILDREN(soid),
+			OID_AUTO, "openmode",
 			CTLFLAG_RW|CTLTYPE_STRING, sc, 0, ufoma_sysctl_open,
 			"A", "Mode to transit when port is opened");
 	SYSCTL_ADD_UINT(sctx, SYSCTL_CHILDREN(soid), OID_AUTO, "comunit",
@@ -476,6 +484,35 @@
 ufoma_detach(device_t dev)
 {
 	struct ufoma_softc *sc = device_get_softc(dev);
+	struct sysctl_ctx_list *sctx;
+
+	/*
+	 * Remove the sysctl OIDs whose handlers dereference sc_modetable
+	 * (ufoma_sysctl_support, ufoma_sysctl_current, ufoma_sysctl_open)
+	 * BEFORE freeing sc_modetable below.  sysctl_remove_oid(…,1,0)
+	 * blocks on the sysctl rwlock until all in-flight handlers drain,
+	 * so after it returns no handler can touch sc_modetable.
+	 *
+	 * sysctl_ctx_entry_del must be called first to drop the stale
+	 * context entry; otherwise device_sysctl_fini → sysctl_ctx_free
+	 * would later iterate a pointer to the freed OID.
+	 */
+	sctx = device_get_sysctl_ctx(dev);
+	if (sc->sc_oid_support != NULL) {
+		sysctl_ctx_entry_del(sctx, sc->sc_oid_support);
+		sysctl_remove_oid(sc->sc_oid_support, 1, 0);
+		sc->sc_oid_support = NULL;
+	}
+	if (sc->sc_oid_current != NULL) {
+		sysctl_ctx_entry_del(sctx, sc->sc_oid_current);
+		sysctl_remove_oid(sc->sc_oid_current, 1, 0);
+		sc->sc_oid_current = NULL;
+	}
+	if (sc->sc_oid_open != NULL) {
+		sysctl_ctx_entry_del(sctx, sc->sc_oid_open);
+		sysctl_remove_oid(sc->sc_oid_open, 1, 0);
+		sc->sc_oid_open = NULL;
+	}
 
 	ucom_detach(&sc->sc_super_ucom, &sc->sc_ucom);
 	usbd_transfer_unsetup(sc->sc_ctrl_xfer, UFOMA_CTRL_ENDPT_MAX);