From: James Bottomley <james.bottom...@hansenpartnership.com>

commit 7862840219058436b80029a0263fd1ef065fb1b3 upstream.

It has been reported that some TIS based TPMs are giving unexpected
errors when using the O_NONBLOCK path of the TPM device. The problem
is that some TPMs don't like it when you get and then relinquish a
locality (as the tpm_try_get_ops()/tpm_put_ops() pair does) without
sending a command.  This currently happens all the time in the
O_NONBLOCK write path. Fix this by moving the tpm_try_get_ops()
further down the code to after the O_NONBLOCK determination is made.
This is safe because the priv->buffer_mutex still protects the priv
state being modified.

BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=206275
Fixes: d23d12484307 ("tpm: fix invalid locking in NONBLOCKING mode")
Reported-by: Mario Limonciello <mario.limoncie...@dell.com>
Tested-by: Alex Guzman <a...@guzman.io>
Cc: sta...@vger.kernel.org
Reviewed-by: Jerry Snitselaar <jsnit...@redhat.com>
Signed-off-by: James Bottomley <james.bottom...@hansenpartnership.com>
Reviewed-by: Jarkko Sakkinen <jarkko.sakki...@linux.intel.com>
Signed-off-by: Jarkko Sakkinen <jarkko.sakki...@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gre...@linuxfoundation.org>

---
 drivers/char/tpm/tpm-dev-common.c |   19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

--- a/drivers/char/tpm/tpm-dev-common.c
+++ b/drivers/char/tpm/tpm-dev-common.c
@@ -189,15 +189,6 @@ ssize_t tpm_common_write(struct file *fi
                goto out;
        }
 
-       /* atomic tpm command send and result receive. We only hold the ops
-        * lock during this period so that the tpm can be unregistered even if
-        * the char dev is held open.
-        */
-       if (tpm_try_get_ops(priv->chip)) {
-               ret = -EPIPE;
-               goto out;
-       }
-
        priv->response_length = 0;
        priv->response_read = false;
        *off = 0;
@@ -211,11 +202,19 @@ ssize_t tpm_common_write(struct file *fi
        if (file->f_flags & O_NONBLOCK) {
                priv->command_enqueued = true;
                queue_work(tpm_dev_wq, &priv->async_work);
-               tpm_put_ops(priv->chip);
                mutex_unlock(&priv->buffer_mutex);
                return size;
        }
 
+       /* atomic tpm command send and result receive. We only hold the ops
+        * lock during this period so that the tpm can be unregistered even if
+        * the char dev is held open.
+        */
+       if (tpm_try_get_ops(priv->chip)) {
+               ret = -EPIPE;
+               goto out;
+       }
+
        ret = tpm_dev_transmit(priv->chip, priv->space, priv->data_buffer,
                               sizeof(priv->data_buffer));
        tpm_put_ops(priv->chip);


Reply via email to