⬢ DragonFlyBSD Kernel Audit
DF-4929 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/crypto/tpm/tpm20.c
+++ b/sys/dev/crypto/tpm/tpm20.c
@@ -77,6 +77,11 @@
 
 	callout_stop(&sc->discard_buffer_callout);
 	lockmgr(&sc->dev_lock, LK_EXCLUSIVE);
+	if (sc->dying) {
+		lockmgr(&sc->dev_lock, LK_RELEASE);
+		return (ENXIO);
+	}
+
 	if (sc->owner_tid != uio->uio_td) {
 		lockmgr(&sc->dev_lock, LK_RELEASE);
 		return (EPERM);
@@ -123,9 +128,14 @@
 
 	lockmgr(&sc->dev_lock, LK_EXCLUSIVE);
 
-	while (sc->pending_data_length != 0)
+	while (sc->pending_data_length != 0 && !sc->dying)
 		cv_wait(&sc->buf_cv, &sc->dev_lock);
 
+	if (sc->dying) {
+		lockmgr(&sc->dev_lock, LK_RELEASE);
+		return (ENXIO);
+	}
+
 	result = uiomove(sc->buf, byte_count, uio);
 	if (result != 0) {
 		lockmgr(&sc->dev_lock, LK_RELEASE);
@@ -155,6 +165,11 @@
 
 	lockmgr(&sc->dev_lock, LK_EXCLUSIVE);
 
+	if (sc->dying) {
+		lockmgr(&sc->dev_lock, LK_RELEASE);
+		return;
+	}
+
 	memset(sc->buf, 0, TPM_BUFSIZE);
 	sc->pending_data_length = 0;
 
@@ -211,18 +226,40 @@
 void
 tpm20_release(struct tpm_sc *sc)
 {
-
+	/*
+	 * Best-effort early drain of the discard callout.  The handler takes
+	 * dev_lock itself, so this must happen outside the lock.
+	 */
 #ifdef TPM_HARVEST
 	callout_drain(&sc->harvest_callout);
 #endif
+	callout_drain(&sc->discard_buffer_callout);
 
-	if (sc->buf != NULL)
+	/* Serialize against in-flight tpm20_read/tpm20_write/transmit. */
+	lockmgr(&sc->dev_lock, LK_EXCLUSIVE);
+	sc->dying = true;
+	cv_broadcast(&sc->buf_cv);
+
+	if (sc->sc_cdev != NULL) {
+		destroy_dev(sc->sc_cdev);
+		sc->sc_cdev = NULL;
+	}
+	if (sc->buf != NULL) {
 		kfree(sc->buf, M_TPM20);
+		sc->buf = NULL;
+	}
+	sc->pending_data_length = 0;
+	lockmgr(&sc->dev_lock, LK_RELEASE);
+
+	/*
+	 * A writer can have armed the callout right before dropping the
+	 * lock; the handler bails on sc->dying so it stays safe against
+	 * the still-intact softc.
+	 */
+	callout_drain(&sc->discard_buffer_callout);
 
-	lockuninit(&sc->dev_lock);
 	cv_destroy(&sc->buf_cv);
-	if (sc->sc_cdev != NULL)
-		destroy_dev(sc->sc_cdev);
+	lockuninit(&sc->dev_lock);
 }
 
 int
--- a/sys/dev/crypto/tpm/tpm20.h
+++ b/sys/dev/crypto/tpm/tpm20.h
@@ -119,6 +119,7 @@
 	uint8_t 	*buf;
 	size_t		pending_data_length;
 	struct thread	*owner_tid;
+	bool		dying;		/* release/detach in progress */
 
 	struct callout 	discard_buffer_callout;
 #ifdef TPM_HARVEST