DF-5007 / fix.diff
--- 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)); |