⬢ DragonFlyBSD Kernel Audit
DF-5007 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/crypto/tpm/tpm20.c
+++ b/sys/dev/crypto/tpm/tpm20.c
@@ -75,12 +75,16 @@
 
 	sc = (struct tpm_sc *)dev->si_drv1;
 
-	callout_stop(&sc->discard_buffer_callout);
 	lockmgr(&sc->dev_lock, LK_EXCLUSIVE);
 	if (sc->owner_tid != uio->uio_td) {
 		lockmgr(&sc->dev_lock, LK_RELEASE);
 		return (EPERM);
 	}
+	/* Only the owner may disarm the discard timer: callout_stop() used to
+	 * run before the owner check, so an EPERM-failing read() permanently
+	 * killed the only recovery that clears pending_data_length, wedging
+	 * every later write(2) in the cv_wait loop below. */
+	callout_stop(&sc->discard_buffer_callout);
 
 	bytes_to_transfer = MIN(sc->pending_data_length, uio->uio_resid);
 	if (bytes_to_transfer > 0) {
@@ -123,8 +127,13 @@
 
 	lockmgr(&sc->dev_lock, LK_EXCLUSIVE);
 
-	while (sc->pending_data_length != 0)
-		cv_wait(&sc->buf_cv, &sc->dev_lock);
+	while (sc->pending_data_length != 0) {
+		result = cv_wait_sig(&sc->buf_cv, &sc->dev_lock);
+		if (result != 0) {
+			lockmgr(&sc->dev_lock, LK_RELEASE);
+			return (result);
+		}
+	}
 
 	result = uiomove(sc->buf, byte_count, uio);
 	if (result != 0) {
@@ -259,8 +268,13 @@
 
 	sc = arg;
 	lockmgr(&sc->dev_lock, LK_EXCLUSIVE);
-	while (sc->pending_data_length != 0)
-		cv_wait(&sc->buf_cv, &sc->dev_lock);
+	while (sc->pending_data_length != 0) {
+		result = cv_wait_sig(&sc->buf_cv, &sc->dev_lock);
+		if (result != 0) {
+			lockmgr(&sc->dev_lock, LK_RELEASE);
+			return;
+		}
+	}
 
 	memcpy(sc->buf, cmd, sizeof(cmd));
 	result = sc->transmit(sc, sizeof(cmd));